roojs-debug.js
[roojs1] / roojs-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12
13
14
15
16 // for old browsers
17 window["undefined"] = window["undefined"];
18
19 /**
20  * @class Roo
21  * Roo core utilities and functions.
22  * @singleton
23  */
24 var Roo = {}; 
25 /**
26  * Copies all the properties of config to obj.
27  * @param {Object} obj The receiver of the properties
28  * @param {Object} config The source of the properties
29  * @param {Object} defaults A different object that will also be applied for default values
30  * @return {Object} returns obj
31  * @member Roo apply
32  */
33
34  
35 Roo.apply = function(o, c, defaults){
36     if(defaults){
37         // no "this" reference for friendly out of scope calls
38         Roo.apply(o, defaults);
39     }
40     if(o && c && typeof c == 'object'){
41         for(var p in c){
42             o[p] = c[p];
43         }
44     }
45     return o;
46 };
47
48
49 (function(){
50     var idSeed = 0;
51     var ua = navigator.userAgent.toLowerCase();
52
53     var isStrict = document.compatMode == "CSS1Compat",
54         isOpera = ua.indexOf("opera") > -1,
55         isSafari = (/webkit|khtml/).test(ua),
56         isIE = ua.indexOf("msie") > -1,
57         isIE7 = ua.indexOf("msie 7") > -1,
58         isGecko = !isSafari && ua.indexOf("gecko") > -1,
59         isBorderBox = isIE && !isStrict,
60         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
61         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
62         isLinux = (ua.indexOf("linux") != -1),
63         isSecure = window.location.href.toLowerCase().indexOf("https") === 0;
64
65     // remove css image flicker
66         if(isIE && !isIE7){
67         try{
68             document.execCommand("BackgroundImageCache", false, true);
69         }catch(e){}
70     }
71
72     Roo.apply(Roo, {
73         /**
74          * True if the browser is in strict mode
75          * @type Boolean
76          */
77         isStrict : isStrict,
78         /**
79          * True if the page is running over SSL
80          * @type Boolean
81          */
82         isSecure : isSecure,
83         /**
84          * True when the document is fully initialized and ready for action
85          * @type Boolean
86          */
87         isReady : false,
88         /**
89          * Turn on debugging output (currently only the factory uses this)
90          * @type Boolean
91          */
92         
93         debug: false,
94
95         /**
96          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
97          * @type Boolean
98          */
99         enableGarbageCollector : true,
100
101         /**
102          * True to automatically purge event listeners after uncaching an element (defaults to false).
103          * Note: this only happens if enableGarbageCollector is true.
104          * @type Boolean
105          */
106         enableListenerCollection:false,
107
108         /**
109          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
110          * the IE insecure content warning (defaults to javascript:false).
111          * @type String
112          */
113         SSL_SECURE_URL : "javascript:false",
114
115         /**
116          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
117          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
118          * @type String
119          */
120         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
121
122         emptyFn : function(){},
123
124         /**
125          * Copies all the properties of config to obj if they don't already exist.
126          * @param {Object} obj The receiver of the properties
127          * @param {Object} config The source of the properties
128          * @return {Object} returns obj
129          */
130         applyIf : function(o, c){
131             if(o && c){
132                 for(var p in c){
133                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
134                 }
135             }
136             return o;
137         },
138
139         /**
140          * Applies event listeners to elements by selectors when the document is ready.
141          * The event name is specified with an @ suffix.
142 <pre><code>
143 Roo.addBehaviors({
144    // add a listener for click on all anchors in element with id foo
145    '#foo a@click' : function(e, t){
146        // do something
147    },
148
149    // add the same listener to multiple selectors (separated by comma BEFORE the @)
150    '#foo a, #bar span.some-class@mouseover' : function(){
151        // do something
152    }
153 });
154 </code></pre>
155          * @param {Object} obj The list of behaviors to apply
156          */
157         addBehaviors : function(o){
158             if(!Roo.isReady){
159                 Roo.onReady(function(){
160                     Roo.addBehaviors(o);
161                 });
162                 return;
163             }
164             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
165             for(var b in o){
166                 var parts = b.split('@');
167                 if(parts[1]){ // for Object prototype breakers
168                     var s = parts[0];
169                     if(!cache[s]){
170                         cache[s] = Roo.select(s);
171                     }
172                     cache[s].on(parts[1], o[b]);
173                 }
174             }
175             cache = null;
176         },
177
178         /**
179          * Generates unique ids. If the element already has an id, it is unchanged
180          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
181          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
182          * @return {String} The generated Id.
183          */
184         id : function(el, prefix){
185             prefix = prefix || "roo-gen";
186             el = Roo.getDom(el);
187             var id = prefix + (++idSeed);
188             return el ? (el.id ? el.id : (el.id = id)) : id;
189         },
190          
191        
192         /**
193          * Extends one class with another class and optionally overrides members with the passed literal. This class
194          * also adds the function "override()" to the class that can be used to override
195          * members on an instance.
196          * @param {Object} subclass The class inheriting the functionality
197          * @param {Object} superclass The class being extended
198          * @param {Object} overrides (optional) A literal with members
199          * @method extend
200          */
201         extend : function(){
202             // inline overrides
203             var io = function(o){
204                 for(var m in o){
205                     this[m] = o[m];
206                 }
207             };
208             return function(sb, sp, overrides){
209                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
210                     overrides = sp;
211                     sp = sb;
212                     sb = function(){sp.apply(this, arguments);};
213                 }
214                 var F = function(){}, sbp, spp = sp.prototype;
215                 F.prototype = spp;
216                 sbp = sb.prototype = new F();
217                 sbp.constructor=sb;
218                 sb.superclass=spp;
219                 
220                 if(spp.constructor == Object.prototype.constructor){
221                     spp.constructor=sp;
222                    
223                 }
224                 
225                 sb.override = function(o){
226                     Roo.override(sb, o);
227                 };
228                 sbp.override = io;
229                 Roo.override(sb, overrides);
230                 return sb;
231             };
232         }(),
233
234         /**
235          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
236          * Usage:<pre><code>
237 Roo.override(MyClass, {
238     newMethod1: function(){
239         // etc.
240     },
241     newMethod2: function(foo){
242         // etc.
243     }
244 });
245  </code></pre>
246          * @param {Object} origclass The class to override
247          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
248          * containing one or more methods.
249          * @method override
250          */
251         override : function(origclass, overrides){
252             if(overrides){
253                 var p = origclass.prototype;
254                 for(var method in overrides){
255                     p[method] = overrides[method];
256                 }
257             }
258         },
259         /**
260          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
261          * <pre><code>
262 Roo.namespace('Company', 'Company.data');
263 Company.Widget = function() { ... }
264 Company.data.CustomStore = function(config) { ... }
265 </code></pre>
266          * @param {String} namespace1
267          * @param {String} namespace2
268          * @param {String} etc
269          * @method namespace
270          */
271         namespace : function(){
272             var a=arguments, o=null, i, j, d, rt;
273             for (i=0; i<a.length; ++i) {
274                 d=a[i].split(".");
275                 rt = d[0];
276                 /** eval:var:o */
277                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
278                 for (j=1; j<d.length; ++j) {
279                     o[d[j]]=o[d[j]] || {};
280                     o=o[d[j]];
281                 }
282             }
283         },
284         /**
285          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
286          * <pre><code>
287 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
288 Roo.factory(conf, Roo.data);
289 </code></pre>
290          * @param {String} classname
291          * @param {String} namespace (optional)
292          * @method factory
293          */
294          
295         factory : function(c, ns)
296         {
297             // no xtype, no ns or c.xns - or forced off by c.xns
298             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
299                 return c;
300             }
301             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
302             if (c.constructor == ns[c.xtype]) {// already created...
303                 return c;
304             }
305             if (ns[c.xtype]) {
306                 if (Roo.debug) Roo.log("Roo.Factory(" + c.xtype + ")");
307                 var ret = new ns[c.xtype](c);
308                 ret.xns = false;
309                 return ret;
310             }
311             c.xns = false; // prevent recursion..
312             return c;
313         },
314          /**
315          * Logs to console if it can.
316          *
317          * @param {String|Object} string
318          * @method log
319          */
320         log : function(s)
321         {
322             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
323                 return; // alerT?
324             }
325             console.log(s);
326             
327         },
328         /**
329          * Takes an object and converts it to an encoded URL. e.g. Roo.urlEncode({foo: 1, bar: 2}); would return "foo=1&bar=2".  Optionally, property values can be arrays, instead of keys and the resulting string that's returned will contain a name/value pair for each array value.
330          * @param {Object} o
331          * @return {String}
332          */
333         urlEncode : function(o){
334             if(!o){
335                 return "";
336             }
337             var buf = [];
338             for(var key in o){
339                 var ov = o[key], k = Roo.encodeURIComponent(key);
340                 var type = typeof ov;
341                 if(type == 'undefined'){
342                     buf.push(k, "=&");
343                 }else if(type != "function" && type != "object"){
344                     buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
345                 }else if(ov instanceof Array){
346                     if (ov.length) {
347                             for(var i = 0, len = ov.length; i < len; i++) {
348                                 buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
349                             }
350                         } else {
351                             buf.push(k, "=&");
352                         }
353                 }
354             }
355             buf.pop();
356             return buf.join("");
357         },
358          /**
359          * Safe version of encodeURIComponent
360          * @param {String} data 
361          * @return {String} 
362          */
363         
364         encodeURIComponent : function (data)
365         {
366             try {
367                 return encodeURIComponent(data);
368             } catch(e) {} // should be an uri encode error.
369             
370             if (data == '' || data == null){
371                return '';
372             }
373             // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
374             function nibble_to_hex(nibble){
375                 var chars = '0123456789ABCDEF';
376                 return chars.charAt(nibble);
377             }
378             data = data.toString();
379             var buffer = '';
380             for(var i=0; i<data.length; i++){
381                 var c = data.charCodeAt(i);
382                 var bs = new Array();
383                 if (c > 0x10000){
384                         // 4 bytes
385                     bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
386                     bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
387                     bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
388                     bs[3] = 0x80 | (c & 0x3F);
389                 }else if (c > 0x800){
390                          // 3 bytes
391                     bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
392                     bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
393                     bs[2] = 0x80 | (c & 0x3F);
394                 }else if (c > 0x80){
395                        // 2 bytes
396                     bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
397                     bs[1] = 0x80 | (c & 0x3F);
398                 }else{
399                         // 1 byte
400                     bs[0] = c;
401                 }
402                 for(var j=0; j<bs.length; j++){
403                     var b = bs[j];
404                     var hex = nibble_to_hex((b & 0xF0) >>> 4) 
405                             + nibble_to_hex(b &0x0F);
406                     buffer += '%'+hex;
407                }
408             }
409             return buffer;    
410              
411         },
412
413         /**
414          * Takes an encoded URL and and converts it to an object. e.g. Roo.urlDecode("foo=1&bar=2"); would return {foo: 1, bar: 2} or Roo.urlDecode("foo=1&bar=2&bar=3&bar=4", true); would return {foo: 1, bar: [2, 3, 4]}.
415          * @param {String} string
416          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
417          * @return {Object} A literal with members
418          */
419         urlDecode : function(string, overwrite){
420             if(!string || !string.length){
421                 return {};
422             }
423             var obj = {};
424             var pairs = string.split('&');
425             var pair, name, value;
426             for(var i = 0, len = pairs.length; i < len; i++){
427                 pair = pairs[i].split('=');
428                 name = decodeURIComponent(pair[0]);
429                 value = decodeURIComponent(pair[1]);
430                 if(overwrite !== true){
431                     if(typeof obj[name] == "undefined"){
432                         obj[name] = value;
433                     }else if(typeof obj[name] == "string"){
434                         obj[name] = [obj[name]];
435                         obj[name].push(value);
436                     }else{
437                         obj[name].push(value);
438                     }
439                 }else{
440                     obj[name] = value;
441                 }
442             }
443             return obj;
444         },
445
446         /**
447          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
448          * passed array is not really an array, your function is called once with it.
449          * The supplied function is called with (Object item, Number index, Array allItems).
450          * @param {Array/NodeList/Mixed} array
451          * @param {Function} fn
452          * @param {Object} scope
453          */
454         each : function(array, fn, scope){
455             if(typeof array.length == "undefined" || typeof array == "string"){
456                 array = [array];
457             }
458             for(var i = 0, len = array.length; i < len; i++){
459                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
460             }
461         },
462
463         // deprecated
464         combine : function(){
465             var as = arguments, l = as.length, r = [];
466             for(var i = 0; i < l; i++){
467                 var a = as[i];
468                 if(a instanceof Array){
469                     r = r.concat(a);
470                 }else if(a.length !== undefined && !a.substr){
471                     r = r.concat(Array.prototype.slice.call(a, 0));
472                 }else{
473                     r.push(a);
474                 }
475             }
476             return r;
477         },
478
479         /**
480          * Escapes the passed string for use in a regular expression
481          * @param {String} str
482          * @return {String}
483          */
484         escapeRe : function(s) {
485             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
486         },
487
488         // internal
489         callback : function(cb, scope, args, delay){
490             if(typeof cb == "function"){
491                 if(delay){
492                     cb.defer(delay, scope, args || []);
493                 }else{
494                     cb.apply(scope, args || []);
495                 }
496             }
497         },
498
499         /**
500          * Return the dom node for the passed string (id), dom node, or Roo.Element
501          * @param {String/HTMLElement/Roo.Element} el
502          * @return HTMLElement
503          */
504         getDom : function(el){
505             if(!el){
506                 return null;
507             }
508             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
509         },
510
511         /**
512         * Shorthand for {@link Roo.ComponentMgr#get}
513         * @param {String} id
514         * @return Roo.Component
515         */
516         getCmp : function(id){
517             return Roo.ComponentMgr.get(id);
518         },
519          
520         num : function(v, defaultValue){
521             if(typeof v != 'number'){
522                 return defaultValue;
523             }
524             return v;
525         },
526
527         destroy : function(){
528             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
529                 var as = a[i];
530                 if(as){
531                     if(as.dom){
532                         as.removeAllListeners();
533                         as.remove();
534                         continue;
535                     }
536                     if(typeof as.purgeListeners == 'function'){
537                         as.purgeListeners();
538                     }
539                     if(typeof as.destroy == 'function'){
540                         as.destroy();
541                     }
542                 }
543             }
544         },
545
546         // inpired by a similar function in mootools library
547         /**
548          * Returns the type of object that is passed in. If the object passed in is null or undefined it
549          * return false otherwise it returns one of the following values:<ul>
550          * <li><b>string</b>: If the object passed is a string</li>
551          * <li><b>number</b>: If the object passed is a number</li>
552          * <li><b>boolean</b>: If the object passed is a boolean value</li>
553          * <li><b>function</b>: If the object passed is a function reference</li>
554          * <li><b>object</b>: If the object passed is an object</li>
555          * <li><b>array</b>: If the object passed is an array</li>
556          * <li><b>regexp</b>: If the object passed is a regular expression</li>
557          * <li><b>element</b>: If the object passed is a DOM Element</li>
558          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
559          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
560          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
561          * @param {Mixed} object
562          * @return {String}
563          */
564         type : function(o){
565             if(o === undefined || o === null){
566                 return false;
567             }
568             if(o.htmlElement){
569                 return 'element';
570             }
571             var t = typeof o;
572             if(t == 'object' && o.nodeName) {
573                 switch(o.nodeType) {
574                     case 1: return 'element';
575                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
576                 }
577             }
578             if(t == 'object' || t == 'function') {
579                 switch(o.constructor) {
580                     case Array: return 'array';
581                     case RegExp: return 'regexp';
582                 }
583                 if(typeof o.length == 'number' && typeof o.item == 'function') {
584                     return 'nodelist';
585                 }
586             }
587             return t;
588         },
589
590         /**
591          * Returns true if the passed value is null, undefined or an empty string (optional).
592          * @param {Mixed} value The value to test
593          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
594          * @return {Boolean}
595          */
596         isEmpty : function(v, allowBlank){
597             return v === null || v === undefined || (!allowBlank ? v === '' : false);
598         },
599         
600         /** @type Boolean */
601         isOpera : isOpera,
602         /** @type Boolean */
603         isSafari : isSafari,
604         /** @type Boolean */
605         isIE : isIE,
606         /** @type Boolean */
607         isIE7 : isIE7,
608         /** @type Boolean */
609         isGecko : isGecko,
610         /** @type Boolean */
611         isBorderBox : isBorderBox,
612         /** @type Boolean */
613         isWindows : isWindows,
614         /** @type Boolean */
615         isLinux : isLinux,
616         /** @type Boolean */
617         isMac : isMac,
618
619         /**
620          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
621          * you may want to set this to true.
622          * @type Boolean
623          */
624         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
625         
626         
627                 
628         /**
629          * Selects a single element as a Roo Element
630          * This is about as close as you can get to jQuery's $('do crazy stuff')
631          * @param {String} selector The selector/xpath query
632          * @param {Node} root (optional) The start of the query (defaults to document).
633          * @return {Roo.Element}
634          */
635         selectNode : function(selector, root) 
636         {
637             var node = Roo.DomQuery.selectNode(selector,root);
638             return node ? Roo.get(node) : new Roo.Element(false);
639         }
640         
641     });
642
643
644 })();
645
646 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
647                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout", "Roo.app", "Roo.ux");
648 /*
649  * Based on:
650  * Ext JS Library 1.1.1
651  * Copyright(c) 2006-2007, Ext JS, LLC.
652  *
653  * Originally Released Under LGPL - original licence link has changed is not relivant.
654  *
655  * Fork - LGPL
656  * <script type="text/javascript">
657  */
658
659 (function() {    
660     // wrappedn so fnCleanup is not in global scope...
661     if(Roo.isIE) {
662         function fnCleanUp() {
663             var p = Function.prototype;
664             delete p.createSequence;
665             delete p.defer;
666             delete p.createDelegate;
667             delete p.createCallback;
668             delete p.createInterceptor;
669
670             window.detachEvent("onunload", fnCleanUp);
671         }
672         window.attachEvent("onunload", fnCleanUp);
673     }
674 })();
675
676
677 /**
678  * @class Function
679  * These functions are available on every Function object (any JavaScript function).
680  */
681 Roo.apply(Function.prototype, {
682      /**
683      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
684      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
685      * Will create a function that is bound to those 2 args.
686      * @return {Function} The new function
687     */
688     createCallback : function(/*args...*/){
689         // make args available, in function below
690         var args = arguments;
691         var method = this;
692         return function() {
693             return method.apply(window, args);
694         };
695     },
696
697     /**
698      * Creates a delegate (callback) that sets the scope to obj.
699      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
700      * Will create a function that is automatically scoped to this.
701      * @param {Object} obj (optional) The object for which the scope is set
702      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
703      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
704      *                                             if a number the args are inserted at the specified position
705      * @return {Function} The new function
706      */
707     createDelegate : function(obj, args, appendArgs){
708         var method = this;
709         return function() {
710             var callArgs = args || arguments;
711             if(appendArgs === true){
712                 callArgs = Array.prototype.slice.call(arguments, 0);
713                 callArgs = callArgs.concat(args);
714             }else if(typeof appendArgs == "number"){
715                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
716                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
717                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
718             }
719             return method.apply(obj || window, callArgs);
720         };
721     },
722
723     /**
724      * Calls this function after the number of millseconds specified.
725      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
726      * @param {Object} obj (optional) The object for which the scope is set
727      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
728      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
729      *                                             if a number the args are inserted at the specified position
730      * @return {Number} The timeout id that can be used with clearTimeout
731      */
732     defer : function(millis, obj, args, appendArgs){
733         var fn = this.createDelegate(obj, args, appendArgs);
734         if(millis){
735             return setTimeout(fn, millis);
736         }
737         fn();
738         return 0;
739     },
740     /**
741      * Create a combined function call sequence of the original function + the passed function.
742      * The resulting function returns the results of the original function.
743      * The passed fcn is called with the parameters of the original function
744      * @param {Function} fcn The function to sequence
745      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
746      * @return {Function} The new function
747      */
748     createSequence : function(fcn, scope){
749         if(typeof fcn != "function"){
750             return this;
751         }
752         var method = this;
753         return function() {
754             var retval = method.apply(this || window, arguments);
755             fcn.apply(scope || this || window, arguments);
756             return retval;
757         };
758     },
759
760     /**
761      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
762      * The resulting function returns the results of the original function.
763      * The passed fcn is called with the parameters of the original function.
764      * @addon
765      * @param {Function} fcn The function to call before the original
766      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
767      * @return {Function} The new function
768      */
769     createInterceptor : function(fcn, scope){
770         if(typeof fcn != "function"){
771             return this;
772         }
773         var method = this;
774         return function() {
775             fcn.target = this;
776             fcn.method = method;
777             if(fcn.apply(scope || this || window, arguments) === false){
778                 return;
779             }
780             return method.apply(this || window, arguments);
781         };
782     }
783 });
784 /*
785  * Based on:
786  * Ext JS Library 1.1.1
787  * Copyright(c) 2006-2007, Ext JS, LLC.
788  *
789  * Originally Released Under LGPL - original licence link has changed is not relivant.
790  *
791  * Fork - LGPL
792  * <script type="text/javascript">
793  */
794
795 Roo.applyIf(String, {
796     
797     /** @scope String */
798     
799     /**
800      * Escapes the passed string for ' and \
801      * @param {String} string The string to escape
802      * @return {String} The escaped string
803      * @static
804      */
805     escape : function(string) {
806         return string.replace(/('|\\)/g, "\\$1");
807     },
808
809     /**
810      * Pads the left side of a string with a specified character.  This is especially useful
811      * for normalizing number and date strings.  Example usage:
812      * <pre><code>
813 var s = String.leftPad('123', 5, '0');
814 // s now contains the string: '00123'
815 </code></pre>
816      * @param {String} string The original string
817      * @param {Number} size The total length of the output string
818      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
819      * @return {String} The padded string
820      * @static
821      */
822     leftPad : function (val, size, ch) {
823         var result = new String(val);
824         if(ch === null || ch === undefined || ch === '') {
825             ch = " ";
826         }
827         while (result.length < size) {
828             result = ch + result;
829         }
830         return result;
831     },
832
833     /**
834      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
835      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
836      * <pre><code>
837 var cls = 'my-class', text = 'Some text';
838 var s = String.format('<div class="{0}">{1}</div>', cls, text);
839 // s now contains the string: '<div class="my-class">Some text</div>'
840 </code></pre>
841      * @param {String} string The tokenized string to be formatted
842      * @param {String} value1 The value to replace token {0}
843      * @param {String} value2 Etc...
844      * @return {String} The formatted string
845      * @static
846      */
847     format : function(format){
848         var args = Array.prototype.slice.call(arguments, 1);
849         return format.replace(/\{(\d+)\}/g, function(m, i){
850             return Roo.util.Format.htmlEncode(args[i]);
851         });
852     }
853 });
854
855 /**
856  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
857  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
858  * they are already different, the first value passed in is returned.  Note that this method returns the new value
859  * but does not change the current string.
860  * <pre><code>
861 // alternate sort directions
862 sort = sort.toggle('ASC', 'DESC');
863
864 // instead of conditional logic:
865 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
866 </code></pre>
867  * @param {String} value The value to compare to the current string
868  * @param {String} other The new value to use if the string already equals the first value passed in
869  * @return {String} The new value
870  */
871  
872 String.prototype.toggle = function(value, other){
873     return this == value ? other : value;
874 };/*
875  * Based on:
876  * Ext JS Library 1.1.1
877  * Copyright(c) 2006-2007, Ext JS, LLC.
878  *
879  * Originally Released Under LGPL - original licence link has changed is not relivant.
880  *
881  * Fork - LGPL
882  * <script type="text/javascript">
883  */
884
885  /**
886  * @class Number
887  */
888 Roo.applyIf(Number.prototype, {
889     /**
890      * Checks whether or not the current number is within a desired range.  If the number is already within the
891      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
892      * exceeded.  Note that this method returns the constrained value but does not change the current number.
893      * @param {Number} min The minimum number in the range
894      * @param {Number} max The maximum number in the range
895      * @return {Number} The constrained value if outside the range, otherwise the current value
896      */
897     constrain : function(min, max){
898         return Math.min(Math.max(this, min), max);
899     }
900 });/*
901  * Based on:
902  * Ext JS Library 1.1.1
903  * Copyright(c) 2006-2007, Ext JS, LLC.
904  *
905  * Originally Released Under LGPL - original licence link has changed is not relivant.
906  *
907  * Fork - LGPL
908  * <script type="text/javascript">
909  */
910  /**
911  * @class Array
912  */
913 Roo.applyIf(Array.prototype, {
914     /**
915      * Checks whether or not the specified object exists in the array.
916      * @param {Object} o The object to check for
917      * @return {Number} The index of o in the array (or -1 if it is not found)
918      */
919     indexOf : function(o){
920        for (var i = 0, len = this.length; i < len; i++){
921               if(this[i] == o) return i;
922        }
923            return -1;
924     },
925
926     /**
927      * Removes the specified object from the array.  If the object is not found nothing happens.
928      * @param {Object} o The object to remove
929      */
930     remove : function(o){
931        var index = this.indexOf(o);
932        if(index != -1){
933            this.splice(index, 1);
934        }
935     },
936     /**
937      * Map (JS 1.6 compatibility)
938      * @param {Function} function  to call
939      */
940     map : function(fun )
941     {
942         var len = this.length >>> 0;
943         if (typeof fun != "function")
944             throw new TypeError();
945
946         var res = new Array(len);
947         var thisp = arguments[1];
948         for (var i = 0; i < len; i++)
949         {
950             if (i in this)
951                 res[i] = fun.call(thisp, this[i], i, this);
952         }
953
954         return res;
955     }
956     
957 });
958
959
960  /*
961  * Based on:
962  * Ext JS Library 1.1.1
963  * Copyright(c) 2006-2007, Ext JS, LLC.
964  *
965  * Originally Released Under LGPL - original licence link has changed is not relivant.
966  *
967  * Fork - LGPL
968  * <script type="text/javascript">
969  */
970
971 /**
972  * @class Date
973  *
974  * The date parsing and format syntax is a subset of
975  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
976  * supported will provide results equivalent to their PHP versions.
977  *
978  * Following is the list of all currently supported formats:
979  *<pre>
980 Sample date:
981 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
982
983 Format  Output      Description
984 ------  ----------  --------------------------------------------------------------
985   d      10         Day of the month, 2 digits with leading zeros
986   D      Wed        A textual representation of a day, three letters
987   j      10         Day of the month without leading zeros
988   l      Wednesday  A full textual representation of the day of the week
989   S      th         English ordinal day of month suffix, 2 chars (use with j)
990   w      3          Numeric representation of the day of the week
991   z      9          The julian date, or day of the year (0-365)
992   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
993   F      January    A full textual representation of the month
994   m      01         Numeric representation of a month, with leading zeros
995   M      Jan        Month name abbreviation, three letters
996   n      1          Numeric representation of a month, without leading zeros
997   t      31         Number of days in the given month
998   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
999   Y      2007       A full numeric representation of a year, 4 digits
1000   y      07         A two digit representation of a year
1001   a      pm         Lowercase Ante meridiem and Post meridiem
1002   A      PM         Uppercase Ante meridiem and Post meridiem
1003   g      3          12-hour format of an hour without leading zeros
1004   G      15         24-hour format of an hour without leading zeros
1005   h      03         12-hour format of an hour with leading zeros
1006   H      15         24-hour format of an hour with leading zeros
1007   i      05         Minutes with leading zeros
1008   s      01         Seconds, with leading zeros
1009   O      -0600      Difference to Greenwich time (GMT) in hours
1010   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1011   T      CST        Timezone setting of the machine running the code
1012   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1013 </pre>
1014  *
1015  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1016  * <pre><code>
1017 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1018 document.write(dt.format('Y-m-d'));                         //2007-01-10
1019 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1020 document.write(dt.format('l, \\t\\he dS of F Y h:i:s A'));  //Wednesday, the 10th of January 2007 03:05:01 PM
1021  </code></pre>
1022  *
1023  * Here are some standard date/time patterns that you might find helpful.  They
1024  * are not part of the source of Date.js, but to use them you can simply copy this
1025  * block of code into any script that is included after Date.js and they will also become
1026  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1027  * <pre><code>
1028 Date.patterns = {
1029     ISO8601Long:"Y-m-d H:i:s",
1030     ISO8601Short:"Y-m-d",
1031     ShortDate: "n/j/Y",
1032     LongDate: "l, F d, Y",
1033     FullDateTime: "l, F d, Y g:i:s A",
1034     MonthDay: "F d",
1035     ShortTime: "g:i A",
1036     LongTime: "g:i:s A",
1037     SortableDateTime: "Y-m-d\\TH:i:s",
1038     UniversalSortableDateTime: "Y-m-d H:i:sO",
1039     YearMonth: "F, Y"
1040 };
1041 </code></pre>
1042  *
1043  * Example usage:
1044  * <pre><code>
1045 var dt = new Date();
1046 document.write(dt.format(Date.patterns.ShortDate));
1047  </code></pre>
1048  */
1049
1050 /*
1051  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1052  * They generate precompiled functions from date formats instead of parsing and
1053  * processing the pattern every time you format a date.  These functions are available
1054  * on every Date object (any javascript function).
1055  *
1056  * The original article and download are here:
1057  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1058  *
1059  */
1060  
1061  
1062  // was in core
1063 /**
1064  Returns the number of milliseconds between this date and date
1065  @param {Date} date (optional) Defaults to now
1066  @return {Number} The diff in milliseconds
1067  @member Date getElapsed
1068  */
1069 Date.prototype.getElapsed = function(date) {
1070         return Math.abs((date || new Date()).getTime()-this.getTime());
1071 };
1072 // was in date file..
1073
1074
1075 // private
1076 Date.parseFunctions = {count:0};
1077 // private
1078 Date.parseRegexes = [];
1079 // private
1080 Date.formatFunctions = {count:0};
1081
1082 // private
1083 Date.prototype.dateFormat = function(format) {
1084     if (Date.formatFunctions[format] == null) {
1085         Date.createNewFormat(format);
1086     }
1087     var func = Date.formatFunctions[format];
1088     return this[func]();
1089 };
1090
1091
1092 /**
1093  * Formats a date given the supplied format string
1094  * @param {String} format The format string
1095  * @return {String} The formatted date
1096  * @method
1097  */
1098 Date.prototype.format = Date.prototype.dateFormat;
1099
1100 // private
1101 Date.createNewFormat = function(format) {
1102     var funcName = "format" + Date.formatFunctions.count++;
1103     Date.formatFunctions[format] = funcName;
1104     var code = "Date.prototype." + funcName + " = function(){return ";
1105     var special = false;
1106     var ch = '';
1107     for (var i = 0; i < format.length; ++i) {
1108         ch = format.charAt(i);
1109         if (!special && ch == "\\") {
1110             special = true;
1111         }
1112         else if (special) {
1113             special = false;
1114             code += "'" + String.escape(ch) + "' + ";
1115         }
1116         else {
1117             code += Date.getFormatCode(ch);
1118         }
1119     }
1120     /** eval:var:zzzzzzzzzzzzz */
1121     eval(code.substring(0, code.length - 3) + ";}");
1122 };
1123
1124 // private
1125 Date.getFormatCode = function(character) {
1126     switch (character) {
1127     case "d":
1128         return "String.leftPad(this.getDate(), 2, '0') + ";
1129     case "D":
1130         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1131     case "j":
1132         return "this.getDate() + ";
1133     case "l":
1134         return "Date.dayNames[this.getDay()] + ";
1135     case "S":
1136         return "this.getSuffix() + ";
1137     case "w":
1138         return "this.getDay() + ";
1139     case "z":
1140         return "this.getDayOfYear() + ";
1141     case "W":
1142         return "this.getWeekOfYear() + ";
1143     case "F":
1144         return "Date.monthNames[this.getMonth()] + ";
1145     case "m":
1146         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1147     case "M":
1148         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1149     case "n":
1150         return "(this.getMonth() + 1) + ";
1151     case "t":
1152         return "this.getDaysInMonth() + ";
1153     case "L":
1154         return "(this.isLeapYear() ? 1 : 0) + ";
1155     case "Y":
1156         return "this.getFullYear() + ";
1157     case "y":
1158         return "('' + this.getFullYear()).substring(2, 4) + ";
1159     case "a":
1160         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1161     case "A":
1162         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1163     case "g":
1164         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1165     case "G":
1166         return "this.getHours() + ";
1167     case "h":
1168         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1169     case "H":
1170         return "String.leftPad(this.getHours(), 2, '0') + ";
1171     case "i":
1172         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1173     case "s":
1174         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1175     case "O":
1176         return "this.getGMTOffset() + ";
1177     case "P":
1178         return "this.getGMTColonOffset() + ";
1179     case "T":
1180         return "this.getTimezone() + ";
1181     case "Z":
1182         return "(this.getTimezoneOffset() * -60) + ";
1183     default:
1184         return "'" + String.escape(character) + "' + ";
1185     }
1186 };
1187
1188 /**
1189  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1190  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1191  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1192  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1193  * string or the parse operation will fail.
1194  * Example Usage:
1195 <pre><code>
1196 //dt = Fri May 25 2007 (current date)
1197 var dt = new Date();
1198
1199 //dt = Thu May 25 2006 (today's month/day in 2006)
1200 dt = Date.parseDate("2006", "Y");
1201
1202 //dt = Sun Jan 15 2006 (all date parts specified)
1203 dt = Date.parseDate("2006-1-15", "Y-m-d");
1204
1205 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1206 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1207 </code></pre>
1208  * @param {String} input The unparsed date as a string
1209  * @param {String} format The format the date is in
1210  * @return {Date} The parsed date
1211  * @static
1212  */
1213 Date.parseDate = function(input, format) {
1214     if (Date.parseFunctions[format] == null) {
1215         Date.createParser(format);
1216     }
1217     var func = Date.parseFunctions[format];
1218     return Date[func](input);
1219 };
1220 /**
1221  * @private
1222  */
1223 Date.createParser = function(format) {
1224     var funcName = "parse" + Date.parseFunctions.count++;
1225     var regexNum = Date.parseRegexes.length;
1226     var currentGroup = 1;
1227     Date.parseFunctions[format] = funcName;
1228
1229     var code = "Date." + funcName + " = function(input){\n"
1230         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1231         + "var d = new Date();\n"
1232         + "y = d.getFullYear();\n"
1233         + "m = d.getMonth();\n"
1234         + "d = d.getDate();\n"
1235         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1236         + "if (results && results.length > 0) {";
1237     var regex = "";
1238
1239     var special = false;
1240     var ch = '';
1241     for (var i = 0; i < format.length; ++i) {
1242         ch = format.charAt(i);
1243         if (!special && ch == "\\") {
1244             special = true;
1245         }
1246         else if (special) {
1247             special = false;
1248             regex += String.escape(ch);
1249         }
1250         else {
1251             var obj = Date.formatCodeToRegex(ch, currentGroup);
1252             currentGroup += obj.g;
1253             regex += obj.s;
1254             if (obj.g && obj.c) {
1255                 code += obj.c;
1256             }
1257         }
1258     }
1259
1260     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1261         + "{v = new Date(y, m, d, h, i, s);}\n"
1262         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1263         + "{v = new Date(y, m, d, h, i);}\n"
1264         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1265         + "{v = new Date(y, m, d, h);}\n"
1266         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1267         + "{v = new Date(y, m, d);}\n"
1268         + "else if (y >= 0 && m >= 0)\n"
1269         + "{v = new Date(y, m);}\n"
1270         + "else if (y >= 0)\n"
1271         + "{v = new Date(y);}\n"
1272         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1273         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1274         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1275         + ";}";
1276
1277     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1278     /** eval:var:zzzzzzzzzzzzz */
1279     eval(code);
1280 };
1281
1282 // private
1283 Date.formatCodeToRegex = function(character, currentGroup) {
1284     switch (character) {
1285     case "D":
1286         return {g:0,
1287         c:null,
1288         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1289     case "j":
1290         return {g:1,
1291             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1292             s:"(\\d{1,2})"}; // day of month without leading zeroes
1293     case "d":
1294         return {g:1,
1295             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1296             s:"(\\d{2})"}; // day of month with leading zeroes
1297     case "l":
1298         return {g:0,
1299             c:null,
1300             s:"(?:" + Date.dayNames.join("|") + ")"};
1301     case "S":
1302         return {g:0,
1303             c:null,
1304             s:"(?:st|nd|rd|th)"};
1305     case "w":
1306         return {g:0,
1307             c:null,
1308             s:"\\d"};
1309     case "z":
1310         return {g:0,
1311             c:null,
1312             s:"(?:\\d{1,3})"};
1313     case "W":
1314         return {g:0,
1315             c:null,
1316             s:"(?:\\d{2})"};
1317     case "F":
1318         return {g:1,
1319             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1320             s:"(" + Date.monthNames.join("|") + ")"};
1321     case "M":
1322         return {g:1,
1323             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1324             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1325     case "n":
1326         return {g:1,
1327             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1328             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1329     case "m":
1330         return {g:1,
1331             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1332             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1333     case "t":
1334         return {g:0,
1335             c:null,
1336             s:"\\d{1,2}"};
1337     case "L":
1338         return {g:0,
1339             c:null,
1340             s:"(?:1|0)"};
1341     case "Y":
1342         return {g:1,
1343             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1344             s:"(\\d{4})"};
1345     case "y":
1346         return {g:1,
1347             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1348                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1349             s:"(\\d{1,2})"};
1350     case "a":
1351         return {g:1,
1352             c:"if (results[" + currentGroup + "] == 'am') {\n"
1353                 + "if (h == 12) { h = 0; }\n"
1354                 + "} else { if (h < 12) { h += 12; }}",
1355             s:"(am|pm)"};
1356     case "A":
1357         return {g:1,
1358             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1359                 + "if (h == 12) { h = 0; }\n"
1360                 + "} else { if (h < 12) { h += 12; }}",
1361             s:"(AM|PM)"};
1362     case "g":
1363     case "G":
1364         return {g:1,
1365             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1366             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1367     case "h":
1368     case "H":
1369         return {g:1,
1370             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1371             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1372     case "i":
1373         return {g:1,
1374             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1375             s:"(\\d{2})"};
1376     case "s":
1377         return {g:1,
1378             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1379             s:"(\\d{2})"};
1380     case "O":
1381         return {g:1,
1382             c:[
1383                 "o = results[", currentGroup, "];\n",
1384                 "var sn = o.substring(0,1);\n", // get + / - sign
1385                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1386                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1387                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1388                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1389             ].join(""),
1390             s:"([+\-]\\d{4})"};
1391     case "P":
1392         return {g:1,
1393                 c:[
1394                    "o = results[", currentGroup, "];\n",
1395                    "var sn = o.substring(0,1);\n",
1396                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1397                    "var mn = o.substring(4,6) % 60;\n",
1398                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1399                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1400             ].join(""),
1401             s:"([+\-]\\d{4})"};
1402     case "T":
1403         return {g:0,
1404             c:null,
1405             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1406     case "Z":
1407         return {g:1,
1408             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1409                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1410             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1411     default:
1412         return {g:0,
1413             c:null,
1414             s:String.escape(character)};
1415     }
1416 };
1417
1418 /**
1419  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1420  * @return {String} The abbreviated timezone name (e.g. 'CST')
1421  */
1422 Date.prototype.getTimezone = function() {
1423     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1424 };
1425
1426 /**
1427  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1428  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1429  */
1430 Date.prototype.getGMTOffset = function() {
1431     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1432         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1433         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1434 };
1435
1436 /**
1437  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1438  * @return {String} 2-characters representing hours and 2-characters representing minutes
1439  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1440  */
1441 Date.prototype.getGMTColonOffset = function() {
1442         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1443                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1444                 + ":"
1445                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1446 }
1447
1448 /**
1449  * Get the numeric day number of the year, adjusted for leap year.
1450  * @return {Number} 0 through 364 (365 in leap years)
1451  */
1452 Date.prototype.getDayOfYear = function() {
1453     var num = 0;
1454     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1455     for (var i = 0; i < this.getMonth(); ++i) {
1456         num += Date.daysInMonth[i];
1457     }
1458     return num + this.getDate() - 1;
1459 };
1460
1461 /**
1462  * Get the string representation of the numeric week number of the year
1463  * (equivalent to the format specifier 'W').
1464  * @return {String} '00' through '52'
1465  */
1466 Date.prototype.getWeekOfYear = function() {
1467     // Skip to Thursday of this week
1468     var now = this.getDayOfYear() + (4 - this.getDay());
1469     // Find the first Thursday of the year
1470     var jan1 = new Date(this.getFullYear(), 0, 1);
1471     var then = (7 - jan1.getDay() + 4);
1472     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1473 };
1474
1475 /**
1476  * Whether or not the current date is in a leap year.
1477  * @return {Boolean} True if the current date is in a leap year, else false
1478  */
1479 Date.prototype.isLeapYear = function() {
1480     var year = this.getFullYear();
1481     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1482 };
1483
1484 /**
1485  * Get the first day of the current month, adjusted for leap year.  The returned value
1486  * is the numeric day index within the week (0-6) which can be used in conjunction with
1487  * the {@link #monthNames} array to retrieve the textual day name.
1488  * Example:
1489  *<pre><code>
1490 var dt = new Date('1/10/2007');
1491 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1492 </code></pre>
1493  * @return {Number} The day number (0-6)
1494  */
1495 Date.prototype.getFirstDayOfMonth = function() {
1496     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1497     return (day < 0) ? (day + 7) : day;
1498 };
1499
1500 /**
1501  * Get the last day of the current month, adjusted for leap year.  The returned value
1502  * is the numeric day index within the week (0-6) which can be used in conjunction with
1503  * the {@link #monthNames} array to retrieve the textual day name.
1504  * Example:
1505  *<pre><code>
1506 var dt = new Date('1/10/2007');
1507 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1508 </code></pre>
1509  * @return {Number} The day number (0-6)
1510  */
1511 Date.prototype.getLastDayOfMonth = function() {
1512     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1513     return (day < 0) ? (day + 7) : day;
1514 };
1515
1516
1517 /**
1518  * Get the first date of this date's month
1519  * @return {Date}
1520  */
1521 Date.prototype.getFirstDateOfMonth = function() {
1522     return new Date(this.getFullYear(), this.getMonth(), 1);
1523 };
1524
1525 /**
1526  * Get the last date of this date's month
1527  * @return {Date}
1528  */
1529 Date.prototype.getLastDateOfMonth = function() {
1530     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1531 };
1532 /**
1533  * Get the number of days in the current month, adjusted for leap year.
1534  * @return {Number} The number of days in the month
1535  */
1536 Date.prototype.getDaysInMonth = function() {
1537     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1538     return Date.daysInMonth[this.getMonth()];
1539 };
1540
1541 /**
1542  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1543  * @return {String} 'st, 'nd', 'rd' or 'th'
1544  */
1545 Date.prototype.getSuffix = function() {
1546     switch (this.getDate()) {
1547         case 1:
1548         case 21:
1549         case 31:
1550             return "st";
1551         case 2:
1552         case 22:
1553             return "nd";
1554         case 3:
1555         case 23:
1556             return "rd";
1557         default:
1558             return "th";
1559     }
1560 };
1561
1562 // private
1563 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1564
1565 /**
1566  * An array of textual month names.
1567  * Override these values for international dates, for example...
1568  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1569  * @type Array
1570  * @static
1571  */
1572 Date.monthNames =
1573    ["January",
1574     "February",
1575     "March",
1576     "April",
1577     "May",
1578     "June",
1579     "July",
1580     "August",
1581     "September",
1582     "October",
1583     "November",
1584     "December"];
1585
1586 /**
1587  * An array of textual day names.
1588  * Override these values for international dates, for example...
1589  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1590  * @type Array
1591  * @static
1592  */
1593 Date.dayNames =
1594    ["Sunday",
1595     "Monday",
1596     "Tuesday",
1597     "Wednesday",
1598     "Thursday",
1599     "Friday",
1600     "Saturday"];
1601
1602 // private
1603 Date.y2kYear = 50;
1604 // private
1605 Date.monthNumbers = {
1606     Jan:0,
1607     Feb:1,
1608     Mar:2,
1609     Apr:3,
1610     May:4,
1611     Jun:5,
1612     Jul:6,
1613     Aug:7,
1614     Sep:8,
1615     Oct:9,
1616     Nov:10,
1617     Dec:11};
1618
1619 /**
1620  * Creates and returns a new Date instance with the exact same date value as the called instance.
1621  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1622  * variable will also be changed.  When the intention is to create a new variable that will not
1623  * modify the original instance, you should create a clone.
1624  *
1625  * Example of correctly cloning a date:
1626  * <pre><code>
1627 //wrong way:
1628 var orig = new Date('10/1/2006');
1629 var copy = orig;
1630 copy.setDate(5);
1631 document.write(orig);  //returns 'Thu Oct 05 2006'!
1632
1633 //correct way:
1634 var orig = new Date('10/1/2006');
1635 var copy = orig.clone();
1636 copy.setDate(5);
1637 document.write(orig);  //returns 'Thu Oct 01 2006'
1638 </code></pre>
1639  * @return {Date} The new Date instance
1640  */
1641 Date.prototype.clone = function() {
1642         return new Date(this.getTime());
1643 };
1644
1645 /**
1646  * Clears any time information from this date
1647  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1648  @return {Date} this or the clone
1649  */
1650 Date.prototype.clearTime = function(clone){
1651     if(clone){
1652         return this.clone().clearTime();
1653     }
1654     this.setHours(0);
1655     this.setMinutes(0);
1656     this.setSeconds(0);
1657     this.setMilliseconds(0);
1658     return this;
1659 };
1660
1661 // private
1662 // safari setMonth is broken
1663 if(Roo.isSafari){
1664     Date.brokenSetMonth = Date.prototype.setMonth;
1665         Date.prototype.setMonth = function(num){
1666                 if(num <= -1){
1667                         var n = Math.ceil(-num);
1668                         var back_year = Math.ceil(n/12);
1669                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1670                         this.setFullYear(this.getFullYear() - back_year);
1671                         return Date.brokenSetMonth.call(this, month);
1672                 } else {
1673                         return Date.brokenSetMonth.apply(this, arguments);
1674                 }
1675         };
1676 }
1677
1678 /** Date interval constant 
1679 * @static 
1680 * @type String */
1681 Date.MILLI = "ms";
1682 /** Date interval constant 
1683 * @static 
1684 * @type String */
1685 Date.SECOND = "s";
1686 /** Date interval constant 
1687 * @static 
1688 * @type String */
1689 Date.MINUTE = "mi";
1690 /** Date interval constant 
1691 * @static 
1692 * @type String */
1693 Date.HOUR = "h";
1694 /** Date interval constant 
1695 * @static 
1696 * @type String */
1697 Date.DAY = "d";
1698 /** Date interval constant 
1699 * @static 
1700 * @type String */
1701 Date.MONTH = "mo";
1702 /** Date interval constant 
1703 * @static 
1704 * @type String */
1705 Date.YEAR = "y";
1706
1707 /**
1708  * Provides a convenient method of performing basic date arithmetic.  This method
1709  * does not modify the Date instance being called - it creates and returns
1710  * a new Date instance containing the resulting date value.
1711  *
1712  * Examples:
1713  * <pre><code>
1714 //Basic usage:
1715 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1716 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1717
1718 //Negative values will subtract correctly:
1719 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1720 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1721
1722 //You can even chain several calls together in one line!
1723 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1724 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1725  </code></pre>
1726  *
1727  * @param {String} interval   A valid date interval enum value
1728  * @param {Number} value      The amount to add to the current date
1729  * @return {Date} The new Date instance
1730  */
1731 Date.prototype.add = function(interval, value){
1732   var d = this.clone();
1733   if (!interval || value === 0) return d;
1734   switch(interval.toLowerCase()){
1735     case Date.MILLI:
1736       d.setMilliseconds(this.getMilliseconds() + value);
1737       break;
1738     case Date.SECOND:
1739       d.setSeconds(this.getSeconds() + value);
1740       break;
1741     case Date.MINUTE:
1742       d.setMinutes(this.getMinutes() + value);
1743       break;
1744     case Date.HOUR:
1745       d.setHours(this.getHours() + value);
1746       break;
1747     case Date.DAY:
1748       d.setDate(this.getDate() + value);
1749       break;
1750     case Date.MONTH:
1751       var day = this.getDate();
1752       if(day > 28){
1753           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1754       }
1755       d.setDate(day);
1756       d.setMonth(this.getMonth() + value);
1757       break;
1758     case Date.YEAR:
1759       d.setFullYear(this.getFullYear() + value);
1760       break;
1761   }
1762   return d;
1763 };
1764 /*
1765  * Based on:
1766  * Ext JS Library 1.1.1
1767  * Copyright(c) 2006-2007, Ext JS, LLC.
1768  *
1769  * Originally Released Under LGPL - original licence link has changed is not relivant.
1770  *
1771  * Fork - LGPL
1772  * <script type="text/javascript">
1773  */
1774
1775 Roo.lib.Dom = {
1776     getViewWidth : function(full) {
1777         return full ? this.getDocumentWidth() : this.getViewportWidth();
1778     },
1779
1780     getViewHeight : function(full) {
1781         return full ? this.getDocumentHeight() : this.getViewportHeight();
1782     },
1783
1784     getDocumentHeight: function() {
1785         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1786         return Math.max(scrollHeight, this.getViewportHeight());
1787     },
1788
1789     getDocumentWidth: function() {
1790         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1791         return Math.max(scrollWidth, this.getViewportWidth());
1792     },
1793
1794     getViewportHeight: function() {
1795         var height = self.innerHeight;
1796         var mode = document.compatMode;
1797
1798         if ((mode || Roo.isIE) && !Roo.isOpera) {
1799             height = (mode == "CSS1Compat") ?
1800                      document.documentElement.clientHeight :
1801                      document.body.clientHeight;
1802         }
1803
1804         return height;
1805     },
1806
1807     getViewportWidth: function() {
1808         var width = self.innerWidth;
1809         var mode = document.compatMode;
1810
1811         if (mode || Roo.isIE) {
1812             width = (mode == "CSS1Compat") ?
1813                     document.documentElement.clientWidth :
1814                     document.body.clientWidth;
1815         }
1816         return width;
1817     },
1818
1819     isAncestor : function(p, c) {
1820         p = Roo.getDom(p);
1821         c = Roo.getDom(c);
1822         if (!p || !c) {
1823             return false;
1824         }
1825
1826         if (p.contains && !Roo.isSafari) {
1827             return p.contains(c);
1828         } else if (p.compareDocumentPosition) {
1829             return !!(p.compareDocumentPosition(c) & 16);
1830         } else {
1831             var parent = c.parentNode;
1832             while (parent) {
1833                 if (parent == p) {
1834                     return true;
1835                 }
1836                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1837                     return false;
1838                 }
1839                 parent = parent.parentNode;
1840             }
1841             return false;
1842         }
1843     },
1844
1845     getRegion : function(el) {
1846         return Roo.lib.Region.getRegion(el);
1847     },
1848
1849     getY : function(el) {
1850         return this.getXY(el)[1];
1851     },
1852
1853     getX : function(el) {
1854         return this.getXY(el)[0];
1855     },
1856
1857     getXY : function(el) {
1858         var p, pe, b, scroll, bd = document.body;
1859         el = Roo.getDom(el);
1860         var fly = Roo.lib.AnimBase.fly;
1861         if (el.getBoundingClientRect) {
1862             b = el.getBoundingClientRect();
1863             scroll = fly(document).getScroll();
1864             return [b.left + scroll.left, b.top + scroll.top];
1865         }
1866         var x = 0, y = 0;
1867
1868         p = el;
1869
1870         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1871
1872         while (p) {
1873
1874             x += p.offsetLeft;
1875             y += p.offsetTop;
1876
1877             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1878                 hasAbsolute = true;
1879             }
1880
1881             if (Roo.isGecko) {
1882                 pe = fly(p);
1883
1884                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1885                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1886
1887
1888                 x += bl;
1889                 y += bt;
1890
1891
1892                 if (p != el && pe.getStyle('overflow') != 'visible') {
1893                     x += bl;
1894                     y += bt;
1895                 }
1896             }
1897             p = p.offsetParent;
1898         }
1899
1900         if (Roo.isSafari && hasAbsolute) {
1901             x -= bd.offsetLeft;
1902             y -= bd.offsetTop;
1903         }
1904
1905         if (Roo.isGecko && !hasAbsolute) {
1906             var dbd = fly(bd);
1907             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
1908             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
1909         }
1910
1911         p = el.parentNode;
1912         while (p && p != bd) {
1913             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
1914                 x -= p.scrollLeft;
1915                 y -= p.scrollTop;
1916             }
1917             p = p.parentNode;
1918         }
1919         return [x, y];
1920     },
1921  
1922   
1923
1924
1925     setXY : function(el, xy) {
1926         el = Roo.fly(el, '_setXY');
1927         el.position();
1928         var pts = el.translatePoints(xy);
1929         if (xy[0] !== false) {
1930             el.dom.style.left = pts.left + "px";
1931         }
1932         if (xy[1] !== false) {
1933             el.dom.style.top = pts.top + "px";
1934         }
1935     },
1936
1937     setX : function(el, x) {
1938         this.setXY(el, [x, false]);
1939     },
1940
1941     setY : function(el, y) {
1942         this.setXY(el, [false, y]);
1943     }
1944 };
1945 /*
1946  * Portions of this file are based on pieces of Yahoo User Interface Library
1947  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
1948  * YUI licensed under the BSD License:
1949  * http://developer.yahoo.net/yui/license.txt
1950  * <script type="text/javascript">
1951  *
1952  */
1953
1954 Roo.lib.Event = function() {
1955     var loadComplete = false;
1956     var listeners = [];
1957     var unloadListeners = [];
1958     var retryCount = 0;
1959     var onAvailStack = [];
1960     var counter = 0;
1961     var lastError = null;
1962
1963     return {
1964         POLL_RETRYS: 200,
1965         POLL_INTERVAL: 20,
1966         EL: 0,
1967         TYPE: 1,
1968         FN: 2,
1969         WFN: 3,
1970         OBJ: 3,
1971         ADJ_SCOPE: 4,
1972         _interval: null,
1973
1974         startInterval: function() {
1975             if (!this._interval) {
1976                 var self = this;
1977                 var callback = function() {
1978                     self._tryPreloadAttach();
1979                 };
1980                 this._interval = setInterval(callback, this.POLL_INTERVAL);
1981
1982             }
1983         },
1984
1985         onAvailable: function(p_id, p_fn, p_obj, p_override) {
1986             onAvailStack.push({ id:         p_id,
1987                 fn:         p_fn,
1988                 obj:        p_obj,
1989                 override:   p_override,
1990                 checkReady: false    });
1991
1992             retryCount = this.POLL_RETRYS;
1993             this.startInterval();
1994         },
1995
1996
1997         addListener: function(el, eventName, fn) {
1998             el = Roo.getDom(el);
1999             if (!el || !fn) {
2000                 return false;
2001             }
2002
2003             if ("unload" == eventName) {
2004                 unloadListeners[unloadListeners.length] =
2005                 [el, eventName, fn];
2006                 return true;
2007             }
2008
2009             var wrappedFn = function(e) {
2010                 return fn(Roo.lib.Event.getEvent(e));
2011             };
2012
2013             var li = [el, eventName, fn, wrappedFn];
2014
2015             var index = listeners.length;
2016             listeners[index] = li;
2017
2018             this.doAdd(el, eventName, wrappedFn, false);
2019             return true;
2020
2021         },
2022
2023
2024         removeListener: function(el, eventName, fn) {
2025             var i, len;
2026
2027             el = Roo.getDom(el);
2028
2029             if(!fn) {
2030                 return this.purgeElement(el, false, eventName);
2031             }
2032
2033
2034             if ("unload" == eventName) {
2035
2036                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2037                     var li = unloadListeners[i];
2038                     if (li &&
2039                         li[0] == el &&
2040                         li[1] == eventName &&
2041                         li[2] == fn) {
2042                         unloadListeners.splice(i, 1);
2043                         return true;
2044                     }
2045                 }
2046
2047                 return false;
2048             }
2049
2050             var cacheItem = null;
2051
2052
2053             var index = arguments[3];
2054
2055             if ("undefined" == typeof index) {
2056                 index = this._getCacheIndex(el, eventName, fn);
2057             }
2058
2059             if (index >= 0) {
2060                 cacheItem = listeners[index];
2061             }
2062
2063             if (!el || !cacheItem) {
2064                 return false;
2065             }
2066
2067             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2068
2069             delete listeners[index][this.WFN];
2070             delete listeners[index][this.FN];
2071             listeners.splice(index, 1);
2072
2073             return true;
2074
2075         },
2076
2077
2078         getTarget: function(ev, resolveTextNode) {
2079             ev = ev.browserEvent || ev;
2080             var t = ev.target || ev.srcElement;
2081             return this.resolveTextNode(t);
2082         },
2083
2084
2085         resolveTextNode: function(node) {
2086             if (Roo.isSafari && node && 3 == node.nodeType) {
2087                 return node.parentNode;
2088             } else {
2089                 return node;
2090             }
2091         },
2092
2093
2094         getPageX: function(ev) {
2095             ev = ev.browserEvent || ev;
2096             var x = ev.pageX;
2097             if (!x && 0 !== x) {
2098                 x = ev.clientX || 0;
2099
2100                 if (Roo.isIE) {
2101                     x += this.getScroll()[1];
2102                 }
2103             }
2104
2105             return x;
2106         },
2107
2108
2109         getPageY: function(ev) {
2110             ev = ev.browserEvent || ev;
2111             var y = ev.pageY;
2112             if (!y && 0 !== y) {
2113                 y = ev.clientY || 0;
2114
2115                 if (Roo.isIE) {
2116                     y += this.getScroll()[0];
2117                 }
2118             }
2119
2120
2121             return y;
2122         },
2123
2124
2125         getXY: function(ev) {
2126             ev = ev.browserEvent || ev;
2127             return [this.getPageX(ev), this.getPageY(ev)];
2128         },
2129
2130
2131         getRelatedTarget: function(ev) {
2132             ev = ev.browserEvent || ev;
2133             var t = ev.relatedTarget;
2134             if (!t) {
2135                 if (ev.type == "mouseout") {
2136                     t = ev.toElement;
2137                 } else if (ev.type == "mouseover") {
2138                     t = ev.fromElement;
2139                 }
2140             }
2141
2142             return this.resolveTextNode(t);
2143         },
2144
2145
2146         getTime: function(ev) {
2147             ev = ev.browserEvent || ev;
2148             if (!ev.time) {
2149                 var t = new Date().getTime();
2150                 try {
2151                     ev.time = t;
2152                 } catch(ex) {
2153                     this.lastError = ex;
2154                     return t;
2155                 }
2156             }
2157
2158             return ev.time;
2159         },
2160
2161
2162         stopEvent: function(ev) {
2163             this.stopPropagation(ev);
2164             this.preventDefault(ev);
2165         },
2166
2167
2168         stopPropagation: function(ev) {
2169             ev = ev.browserEvent || ev;
2170             if (ev.stopPropagation) {
2171                 ev.stopPropagation();
2172             } else {
2173                 ev.cancelBubble = true;
2174             }
2175         },
2176
2177
2178         preventDefault: function(ev) {
2179             ev = ev.browserEvent || ev;
2180             if(ev.preventDefault) {
2181                 ev.preventDefault();
2182             } else {
2183                 ev.returnValue = false;
2184             }
2185         },
2186
2187
2188         getEvent: function(e) {
2189             var ev = e || window.event;
2190             if (!ev) {
2191                 var c = this.getEvent.caller;
2192                 while (c) {
2193                     ev = c.arguments[0];
2194                     if (ev && Event == ev.constructor) {
2195                         break;
2196                     }
2197                     c = c.caller;
2198                 }
2199             }
2200             return ev;
2201         },
2202
2203
2204         getCharCode: function(ev) {
2205             ev = ev.browserEvent || ev;
2206             return ev.charCode || ev.keyCode || 0;
2207         },
2208
2209
2210         _getCacheIndex: function(el, eventName, fn) {
2211             for (var i = 0,len = listeners.length; i < len; ++i) {
2212                 var li = listeners[i];
2213                 if (li &&
2214                     li[this.FN] == fn &&
2215                     li[this.EL] == el &&
2216                     li[this.TYPE] == eventName) {
2217                     return i;
2218                 }
2219             }
2220
2221             return -1;
2222         },
2223
2224
2225         elCache: {},
2226
2227
2228         getEl: function(id) {
2229             return document.getElementById(id);
2230         },
2231
2232
2233         clearCache: function() {
2234         },
2235
2236
2237         _load: function(e) {
2238             loadComplete = true;
2239             var EU = Roo.lib.Event;
2240
2241
2242             if (Roo.isIE) {
2243                 EU.doRemove(window, "load", EU._load);
2244             }
2245         },
2246
2247
2248         _tryPreloadAttach: function() {
2249
2250             if (this.locked) {
2251                 return false;
2252             }
2253
2254             this.locked = true;
2255
2256
2257             var tryAgain = !loadComplete;
2258             if (!tryAgain) {
2259                 tryAgain = (retryCount > 0);
2260             }
2261
2262
2263             var notAvail = [];
2264             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2265                 var item = onAvailStack[i];
2266                 if (item) {
2267                     var el = this.getEl(item.id);
2268
2269                     if (el) {
2270                         if (!item.checkReady ||
2271                             loadComplete ||
2272                             el.nextSibling ||
2273                             (document && document.body)) {
2274
2275                             var scope = el;
2276                             if (item.override) {
2277                                 if (item.override === true) {
2278                                     scope = item.obj;
2279                                 } else {
2280                                     scope = item.override;
2281                                 }
2282                             }
2283                             item.fn.call(scope, item.obj);
2284                             onAvailStack[i] = null;
2285                         }
2286                     } else {
2287                         notAvail.push(item);
2288                     }
2289                 }
2290             }
2291
2292             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2293
2294             if (tryAgain) {
2295
2296                 this.startInterval();
2297             } else {
2298                 clearInterval(this._interval);
2299                 this._interval = null;
2300             }
2301
2302             this.locked = false;
2303
2304             return true;
2305
2306         },
2307
2308
2309         purgeElement: function(el, recurse, eventName) {
2310             var elListeners = this.getListeners(el, eventName);
2311             if (elListeners) {
2312                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2313                     var l = elListeners[i];
2314                     this.removeListener(el, l.type, l.fn);
2315                 }
2316             }
2317
2318             if (recurse && el && el.childNodes) {
2319                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2320                     this.purgeElement(el.childNodes[i], recurse, eventName);
2321                 }
2322             }
2323         },
2324
2325
2326         getListeners: function(el, eventName) {
2327             var results = [], searchLists;
2328             if (!eventName) {
2329                 searchLists = [listeners, unloadListeners];
2330             } else if (eventName == "unload") {
2331                 searchLists = [unloadListeners];
2332             } else {
2333                 searchLists = [listeners];
2334             }
2335
2336             for (var j = 0; j < searchLists.length; ++j) {
2337                 var searchList = searchLists[j];
2338                 if (searchList && searchList.length > 0) {
2339                     for (var i = 0,len = searchList.length; i < len; ++i) {
2340                         var l = searchList[i];
2341                         if (l && l[this.EL] === el &&
2342                             (!eventName || eventName === l[this.TYPE])) {
2343                             results.push({
2344                                 type:   l[this.TYPE],
2345                                 fn:     l[this.FN],
2346                                 obj:    l[this.OBJ],
2347                                 adjust: l[this.ADJ_SCOPE],
2348                                 index:  i
2349                             });
2350                         }
2351                     }
2352                 }
2353             }
2354
2355             return (results.length) ? results : null;
2356         },
2357
2358
2359         _unload: function(e) {
2360
2361             var EU = Roo.lib.Event, i, j, l, len, index;
2362
2363             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2364                 l = unloadListeners[i];
2365                 if (l) {
2366                     var scope = window;
2367                     if (l[EU.ADJ_SCOPE]) {
2368                         if (l[EU.ADJ_SCOPE] === true) {
2369                             scope = l[EU.OBJ];
2370                         } else {
2371                             scope = l[EU.ADJ_SCOPE];
2372                         }
2373                     }
2374                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2375                     unloadListeners[i] = null;
2376                     l = null;
2377                     scope = null;
2378                 }
2379             }
2380
2381             unloadListeners = null;
2382
2383             if (listeners && listeners.length > 0) {
2384                 j = listeners.length;
2385                 while (j) {
2386                     index = j - 1;
2387                     l = listeners[index];
2388                     if (l) {
2389                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2390                                 l[EU.FN], index);
2391                     }
2392                     j = j - 1;
2393                 }
2394                 l = null;
2395
2396                 EU.clearCache();
2397             }
2398
2399             EU.doRemove(window, "unload", EU._unload);
2400
2401         },
2402
2403
2404         getScroll: function() {
2405             var dd = document.documentElement, db = document.body;
2406             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2407                 return [dd.scrollTop, dd.scrollLeft];
2408             } else if (db) {
2409                 return [db.scrollTop, db.scrollLeft];
2410             } else {
2411                 return [0, 0];
2412             }
2413         },
2414
2415
2416         doAdd: function () {
2417             if (window.addEventListener) {
2418                 return function(el, eventName, fn, capture) {
2419                     el.addEventListener(eventName, fn, (capture));
2420                 };
2421             } else if (window.attachEvent) {
2422                 return function(el, eventName, fn, capture) {
2423                     el.attachEvent("on" + eventName, fn);
2424                 };
2425             } else {
2426                 return function() {
2427                 };
2428             }
2429         }(),
2430
2431
2432         doRemove: function() {
2433             if (window.removeEventListener) {
2434                 return function (el, eventName, fn, capture) {
2435                     el.removeEventListener(eventName, fn, (capture));
2436                 };
2437             } else if (window.detachEvent) {
2438                 return function (el, eventName, fn) {
2439                     el.detachEvent("on" + eventName, fn);
2440                 };
2441             } else {
2442                 return function() {
2443                 };
2444             }
2445         }()
2446     };
2447     
2448 }();
2449 (function() {     
2450    
2451     var E = Roo.lib.Event;
2452     E.on = E.addListener;
2453     E.un = E.removeListener;
2454
2455     if (document && document.body) {
2456         E._load();
2457     } else {
2458         E.doAdd(window, "load", E._load);
2459     }
2460     E.doAdd(window, "unload", E._unload);
2461     E._tryPreloadAttach();
2462 })();
2463
2464 /*
2465  * Portions of this file are based on pieces of Yahoo User Interface Library
2466  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2467  * YUI licensed under the BSD License:
2468  * http://developer.yahoo.net/yui/license.txt
2469  * <script type="text/javascript">
2470  *
2471  */
2472
2473 (function() {
2474     
2475     Roo.lib.Ajax = {
2476         request : function(method, uri, cb, data, options) {
2477             if(options){
2478                 var hs = options.headers;
2479                 if(hs){
2480                     for(var h in hs){
2481                         if(hs.hasOwnProperty(h)){
2482                             this.initHeader(h, hs[h], false);
2483                         }
2484                     }
2485                 }
2486                 if(options.xmlData){
2487                     this.initHeader('Content-Type', 'text/xml', false);
2488                     method = 'POST';
2489                     data = options.xmlData;
2490                 }
2491             }
2492
2493             return this.asyncRequest(method, uri, cb, data);
2494         },
2495
2496         serializeForm : function(form) {
2497             if(typeof form == 'string') {
2498                 form = (document.getElementById(form) || document.forms[form]);
2499             }
2500
2501             var el, name, val, disabled, data = '', hasSubmit = false;
2502             for (var i = 0; i < form.elements.length; i++) {
2503                 el = form.elements[i];
2504                 disabled = form.elements[i].disabled;
2505                 name = form.elements[i].name;
2506                 val = form.elements[i].value;
2507
2508                 if (!disabled && name){
2509                     switch (el.type)
2510                             {
2511                         case 'select-one':
2512                         case 'select-multiple':
2513                             for (var j = 0; j < el.options.length; j++) {
2514                                 if (el.options[j].selected) {
2515                                     if (Roo.isIE) {
2516                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2517                                     }
2518                                     else {
2519                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2520                                     }
2521                                 }
2522                             }
2523                             break;
2524                         case 'radio':
2525                         case 'checkbox':
2526                             if (el.checked) {
2527                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2528                             }
2529                             break;
2530                         case 'file':
2531
2532                         case undefined:
2533
2534                         case 'reset':
2535
2536                         case 'button':
2537
2538                             break;
2539                         case 'submit':
2540                             if(hasSubmit == false) {
2541                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2542                                 hasSubmit = true;
2543                             }
2544                             break;
2545                         default:
2546                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2547                             break;
2548                     }
2549                 }
2550             }
2551             data = data.substr(0, data.length - 1);
2552             return data;
2553         },
2554
2555         headers:{},
2556
2557         hasHeaders:false,
2558
2559         useDefaultHeader:true,
2560
2561         defaultPostHeader:'application/x-www-form-urlencoded',
2562
2563         useDefaultXhrHeader:true,
2564
2565         defaultXhrHeader:'XMLHttpRequest',
2566
2567         hasDefaultHeaders:true,
2568
2569         defaultHeaders:{},
2570
2571         poll:{},
2572
2573         timeout:{},
2574
2575         pollInterval:50,
2576
2577         transactionId:0,
2578
2579         setProgId:function(id)
2580         {
2581             this.activeX.unshift(id);
2582         },
2583
2584         setDefaultPostHeader:function(b)
2585         {
2586             this.useDefaultHeader = b;
2587         },
2588
2589         setDefaultXhrHeader:function(b)
2590         {
2591             this.useDefaultXhrHeader = b;
2592         },
2593
2594         setPollingInterval:function(i)
2595         {
2596             if (typeof i == 'number' && isFinite(i)) {
2597                 this.pollInterval = i;
2598             }
2599         },
2600
2601         createXhrObject:function(transactionId)
2602         {
2603             var obj,http;
2604             try
2605             {
2606
2607                 http = new XMLHttpRequest();
2608
2609                 obj = { conn:http, tId:transactionId };
2610             }
2611             catch(e)
2612             {
2613                 for (var i = 0; i < this.activeX.length; ++i) {
2614                     try
2615                     {
2616
2617                         http = new ActiveXObject(this.activeX[i]);
2618
2619                         obj = { conn:http, tId:transactionId };
2620                         break;
2621                     }
2622                     catch(e) {
2623                     }
2624                 }
2625             }
2626             finally
2627             {
2628                 return obj;
2629             }
2630         },
2631
2632         getConnectionObject:function()
2633         {
2634             var o;
2635             var tId = this.transactionId;
2636
2637             try
2638             {
2639                 o = this.createXhrObject(tId);
2640                 if (o) {
2641                     this.transactionId++;
2642                 }
2643             }
2644             catch(e) {
2645             }
2646             finally
2647             {
2648                 return o;
2649             }
2650         },
2651
2652         asyncRequest:function(method, uri, callback, postData)
2653         {
2654             var o = this.getConnectionObject();
2655
2656             if (!o) {
2657                 return null;
2658             }
2659             else {
2660                 o.conn.open(method, uri, true);
2661
2662                 if (this.useDefaultXhrHeader) {
2663                     if (!this.defaultHeaders['X-Requested-With']) {
2664                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2665                     }
2666                 }
2667
2668                 if(postData && this.useDefaultHeader){
2669                     this.initHeader('Content-Type', this.defaultPostHeader);
2670                 }
2671
2672                  if (this.hasDefaultHeaders || this.hasHeaders) {
2673                     this.setHeader(o);
2674                 }
2675
2676                 this.handleReadyState(o, callback);
2677                 o.conn.send(postData || null);
2678
2679                 return o;
2680             }
2681         },
2682
2683         handleReadyState:function(o, callback)
2684         {
2685             var oConn = this;
2686
2687             if (callback && callback.timeout) {
2688                 this.timeout[o.tId] = window.setTimeout(function() {
2689                     oConn.abort(o, callback, true);
2690                 }, callback.timeout);
2691             }
2692
2693             this.poll[o.tId] = window.setInterval(
2694                     function() {
2695                         if (o.conn && o.conn.readyState == 4) {
2696                             window.clearInterval(oConn.poll[o.tId]);
2697                             delete oConn.poll[o.tId];
2698
2699                             if(callback && callback.timeout) {
2700                                 window.clearTimeout(oConn.timeout[o.tId]);
2701                                 delete oConn.timeout[o.tId];
2702                             }
2703
2704                             oConn.handleTransactionResponse(o, callback);
2705                         }
2706                     }
2707                     , this.pollInterval);
2708         },
2709
2710         handleTransactionResponse:function(o, callback, isAbort)
2711         {
2712
2713             if (!callback) {
2714                 this.releaseObject(o);
2715                 return;
2716             }
2717
2718             var httpStatus, responseObject;
2719
2720             try
2721             {
2722                 if (o.conn.status !== undefined && o.conn.status != 0) {
2723                     httpStatus = o.conn.status;
2724                 }
2725                 else {
2726                     httpStatus = 13030;
2727                 }
2728             }
2729             catch(e) {
2730
2731
2732                 httpStatus = 13030;
2733             }
2734
2735             if (httpStatus >= 200 && httpStatus < 300) {
2736                 responseObject = this.createResponseObject(o, callback.argument);
2737                 if (callback.success) {
2738                     if (!callback.scope) {
2739                         callback.success(responseObject);
2740                     }
2741                     else {
2742
2743
2744                         callback.success.apply(callback.scope, [responseObject]);
2745                     }
2746                 }
2747             }
2748             else {
2749                 switch (httpStatus) {
2750
2751                     case 12002:
2752                     case 12029:
2753                     case 12030:
2754                     case 12031:
2755                     case 12152:
2756                     case 13030:
2757                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2758                         if (callback.failure) {
2759                             if (!callback.scope) {
2760                                 callback.failure(responseObject);
2761                             }
2762                             else {
2763                                 callback.failure.apply(callback.scope, [responseObject]);
2764                             }
2765                         }
2766                         break;
2767                     default:
2768                         responseObject = this.createResponseObject(o, callback.argument);
2769                         if (callback.failure) {
2770                             if (!callback.scope) {
2771                                 callback.failure(responseObject);
2772                             }
2773                             else {
2774                                 callback.failure.apply(callback.scope, [responseObject]);
2775                             }
2776                         }
2777                 }
2778             }
2779
2780             this.releaseObject(o);
2781             responseObject = null;
2782         },
2783
2784         createResponseObject:function(o, callbackArg)
2785         {
2786             var obj = {};
2787             var headerObj = {};
2788
2789             try
2790             {
2791                 var headerStr = o.conn.getAllResponseHeaders();
2792                 var header = headerStr.split('\n');
2793                 for (var i = 0; i < header.length; i++) {
2794                     var delimitPos = header[i].indexOf(':');
2795                     if (delimitPos != -1) {
2796                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2797                     }
2798                 }
2799             }
2800             catch(e) {
2801             }
2802
2803             obj.tId = o.tId;
2804             obj.status = o.conn.status;
2805             obj.statusText = o.conn.statusText;
2806             obj.getResponseHeader = headerObj;
2807             obj.getAllResponseHeaders = headerStr;
2808             obj.responseText = o.conn.responseText;
2809             obj.responseXML = o.conn.responseXML;
2810
2811             if (typeof callbackArg !== undefined) {
2812                 obj.argument = callbackArg;
2813             }
2814
2815             return obj;
2816         },
2817
2818         createExceptionObject:function(tId, callbackArg, isAbort)
2819         {
2820             var COMM_CODE = 0;
2821             var COMM_ERROR = 'communication failure';
2822             var ABORT_CODE = -1;
2823             var ABORT_ERROR = 'transaction aborted';
2824
2825             var obj = {};
2826
2827             obj.tId = tId;
2828             if (isAbort) {
2829                 obj.status = ABORT_CODE;
2830                 obj.statusText = ABORT_ERROR;
2831             }
2832             else {
2833                 obj.status = COMM_CODE;
2834                 obj.statusText = COMM_ERROR;
2835             }
2836
2837             if (callbackArg) {
2838                 obj.argument = callbackArg;
2839             }
2840
2841             return obj;
2842         },
2843
2844         initHeader:function(label, value, isDefault)
2845         {
2846             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2847
2848             if (headerObj[label] === undefined) {
2849                 headerObj[label] = value;
2850             }
2851             else {
2852
2853
2854                 headerObj[label] = value + "," + headerObj[label];
2855             }
2856
2857             if (isDefault) {
2858                 this.hasDefaultHeaders = true;
2859             }
2860             else {
2861                 this.hasHeaders = true;
2862             }
2863         },
2864
2865
2866         setHeader:function(o)
2867         {
2868             if (this.hasDefaultHeaders) {
2869                 for (var prop in this.defaultHeaders) {
2870                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2871                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2872                     }
2873                 }
2874             }
2875
2876             if (this.hasHeaders) {
2877                 for (var prop in this.headers) {
2878                     if (this.headers.hasOwnProperty(prop)) {
2879                         o.conn.setRequestHeader(prop, this.headers[prop]);
2880                     }
2881                 }
2882                 this.headers = {};
2883                 this.hasHeaders = false;
2884             }
2885         },
2886
2887         resetDefaultHeaders:function() {
2888             delete this.defaultHeaders;
2889             this.defaultHeaders = {};
2890             this.hasDefaultHeaders = false;
2891         },
2892
2893         abort:function(o, callback, isTimeout)
2894         {
2895             if(this.isCallInProgress(o)) {
2896                 o.conn.abort();
2897                 window.clearInterval(this.poll[o.tId]);
2898                 delete this.poll[o.tId];
2899                 if (isTimeout) {
2900                     delete this.timeout[o.tId];
2901                 }
2902
2903                 this.handleTransactionResponse(o, callback, true);
2904
2905                 return true;
2906             }
2907             else {
2908                 return false;
2909             }
2910         },
2911
2912
2913         isCallInProgress:function(o)
2914         {
2915             if (o && o.conn) {
2916                 return o.conn.readyState != 4 && o.conn.readyState != 0;
2917             }
2918             else {
2919
2920                 return false;
2921             }
2922         },
2923
2924
2925         releaseObject:function(o)
2926         {
2927
2928             o.conn = null;
2929
2930             o = null;
2931         },
2932
2933         activeX:[
2934         'MSXML2.XMLHTTP.3.0',
2935         'MSXML2.XMLHTTP',
2936         'Microsoft.XMLHTTP'
2937         ]
2938
2939
2940     };
2941 })();/*
2942  * Portions of this file are based on pieces of Yahoo User Interface Library
2943  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2944  * YUI licensed under the BSD License:
2945  * http://developer.yahoo.net/yui/license.txt
2946  * <script type="text/javascript">
2947  *
2948  */
2949
2950 Roo.lib.Region = function(t, r, b, l) {
2951     this.top = t;
2952     this[1] = t;
2953     this.right = r;
2954     this.bottom = b;
2955     this.left = l;
2956     this[0] = l;
2957 };
2958
2959
2960 Roo.lib.Region.prototype = {
2961     contains : function(region) {
2962         return ( region.left >= this.left &&
2963                  region.right <= this.right &&
2964                  region.top >= this.top &&
2965                  region.bottom <= this.bottom    );
2966
2967     },
2968
2969     getArea : function() {
2970         return ( (this.bottom - this.top) * (this.right - this.left) );
2971     },
2972
2973     intersect : function(region) {
2974         var t = Math.max(this.top, region.top);
2975         var r = Math.min(this.right, region.right);
2976         var b = Math.min(this.bottom, region.bottom);
2977         var l = Math.max(this.left, region.left);
2978
2979         if (b >= t && r >= l) {
2980             return new Roo.lib.Region(t, r, b, l);
2981         } else {
2982             return null;
2983         }
2984     },
2985     union : function(region) {
2986         var t = Math.min(this.top, region.top);
2987         var r = Math.max(this.right, region.right);
2988         var b = Math.max(this.bottom, region.bottom);
2989         var l = Math.min(this.left, region.left);
2990
2991         return new Roo.lib.Region(t, r, b, l);
2992     },
2993
2994     adjust : function(t, l, b, r) {
2995         this.top += t;
2996         this.left += l;
2997         this.right += r;
2998         this.bottom += b;
2999         return this;
3000     }
3001 };
3002
3003 Roo.lib.Region.getRegion = function(el) {
3004     var p = Roo.lib.Dom.getXY(el);
3005
3006     var t = p[1];
3007     var r = p[0] + el.offsetWidth;
3008     var b = p[1] + el.offsetHeight;
3009     var l = p[0];
3010
3011     return new Roo.lib.Region(t, r, b, l);
3012 };
3013 /*
3014  * Portions of this file are based on pieces of Yahoo User Interface Library
3015  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3016  * YUI licensed under the BSD License:
3017  * http://developer.yahoo.net/yui/license.txt
3018  * <script type="text/javascript">
3019  *
3020  */
3021 //@@dep Roo.lib.Region
3022
3023
3024 Roo.lib.Point = function(x, y) {
3025     if (x instanceof Array) {
3026         y = x[1];
3027         x = x[0];
3028     }
3029     this.x = this.right = this.left = this[0] = x;
3030     this.y = this.top = this.bottom = this[1] = y;
3031 };
3032
3033 Roo.lib.Point.prototype = new Roo.lib.Region();
3034 /*
3035  * Portions of this file are based on pieces of Yahoo User Interface Library
3036  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3037  * YUI licensed under the BSD License:
3038  * http://developer.yahoo.net/yui/license.txt
3039  * <script type="text/javascript">
3040  *
3041  */
3042  
3043 (function() {   
3044
3045     Roo.lib.Anim = {
3046         scroll : function(el, args, duration, easing, cb, scope) {
3047             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3048         },
3049
3050         motion : function(el, args, duration, easing, cb, scope) {
3051             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3052         },
3053
3054         color : function(el, args, duration, easing, cb, scope) {
3055             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3056         },
3057
3058         run : function(el, args, duration, easing, cb, scope, type) {
3059             type = type || Roo.lib.AnimBase;
3060             if (typeof easing == "string") {
3061                 easing = Roo.lib.Easing[easing];
3062             }
3063             var anim = new type(el, args, duration, easing);
3064             anim.animateX(function() {
3065                 Roo.callback(cb, scope);
3066             });
3067             return anim;
3068         }
3069     };
3070 })();/*
3071  * Portions of this file are based on pieces of Yahoo User Interface Library
3072  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3073  * YUI licensed under the BSD License:
3074  * http://developer.yahoo.net/yui/license.txt
3075  * <script type="text/javascript">
3076  *
3077  */
3078
3079 (function() {    
3080     var libFlyweight;
3081     
3082     function fly(el) {
3083         if (!libFlyweight) {
3084             libFlyweight = new Roo.Element.Flyweight();
3085         }
3086         libFlyweight.dom = el;
3087         return libFlyweight;
3088     }
3089
3090     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3091     
3092    
3093     
3094     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3095         if (el) {
3096             this.init(el, attributes, duration, method);
3097         }
3098     };
3099
3100     Roo.lib.AnimBase.fly = fly;
3101     
3102     
3103     
3104     Roo.lib.AnimBase.prototype = {
3105
3106         toString: function() {
3107             var el = this.getEl();
3108             var id = el.id || el.tagName;
3109             return ("Anim " + id);
3110         },
3111
3112         patterns: {
3113             noNegatives:        /width|height|opacity|padding/i,
3114             offsetAttribute:  /^((width|height)|(top|left))$/,
3115             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3116             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3117         },
3118
3119
3120         doMethod: function(attr, start, end) {
3121             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3122         },
3123
3124
3125         setAttribute: function(attr, val, unit) {
3126             if (this.patterns.noNegatives.test(attr)) {
3127                 val = (val > 0) ? val : 0;
3128             }
3129
3130             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3131         },
3132
3133
3134         getAttribute: function(attr) {
3135             var el = this.getEl();
3136             var val = fly(el).getStyle(attr);
3137
3138             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3139                 return parseFloat(val);
3140             }
3141
3142             var a = this.patterns.offsetAttribute.exec(attr) || [];
3143             var pos = !!( a[3] );
3144             var box = !!( a[2] );
3145
3146
3147             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3148                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3149             } else {
3150                 val = 0;
3151             }
3152
3153             return val;
3154         },
3155
3156
3157         getDefaultUnit: function(attr) {
3158             if (this.patterns.defaultUnit.test(attr)) {
3159                 return 'px';
3160             }
3161
3162             return '';
3163         },
3164
3165         animateX : function(callback, scope) {
3166             var f = function() {
3167                 this.onComplete.removeListener(f);
3168                 if (typeof callback == "function") {
3169                     callback.call(scope || this, this);
3170                 }
3171             };
3172             this.onComplete.addListener(f, this);
3173             this.animate();
3174         },
3175
3176
3177         setRuntimeAttribute: function(attr) {
3178             var start;
3179             var end;
3180             var attributes = this.attributes;
3181
3182             this.runtimeAttributes[attr] = {};
3183
3184             var isset = function(prop) {
3185                 return (typeof prop !== 'undefined');
3186             };
3187
3188             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3189                 return false;
3190             }
3191
3192             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3193
3194
3195             if (isset(attributes[attr]['to'])) {
3196                 end = attributes[attr]['to'];
3197             } else if (isset(attributes[attr]['by'])) {
3198                 if (start.constructor == Array) {
3199                     end = [];
3200                     for (var i = 0, len = start.length; i < len; ++i) {
3201                         end[i] = start[i] + attributes[attr]['by'][i];
3202                     }
3203                 } else {
3204                     end = start + attributes[attr]['by'];
3205                 }
3206             }
3207
3208             this.runtimeAttributes[attr].start = start;
3209             this.runtimeAttributes[attr].end = end;
3210
3211
3212             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3213         },
3214
3215
3216         init: function(el, attributes, duration, method) {
3217
3218             var isAnimated = false;
3219
3220
3221             var startTime = null;
3222
3223
3224             var actualFrames = 0;
3225
3226
3227             el = Roo.getDom(el);
3228
3229
3230             this.attributes = attributes || {};
3231
3232
3233             this.duration = duration || 1;
3234
3235
3236             this.method = method || Roo.lib.Easing.easeNone;
3237
3238
3239             this.useSeconds = true;
3240
3241
3242             this.currentFrame = 0;
3243
3244
3245             this.totalFrames = Roo.lib.AnimMgr.fps;
3246
3247
3248             this.getEl = function() {
3249                 return el;
3250             };
3251
3252
3253             this.isAnimated = function() {
3254                 return isAnimated;
3255             };
3256
3257
3258             this.getStartTime = function() {
3259                 return startTime;
3260             };
3261
3262             this.runtimeAttributes = {};
3263
3264
3265             this.animate = function() {
3266                 if (this.isAnimated()) {
3267                     return false;
3268                 }
3269
3270                 this.currentFrame = 0;
3271
3272                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3273
3274                 Roo.lib.AnimMgr.registerElement(this);
3275             };
3276
3277
3278             this.stop = function(finish) {
3279                 if (finish) {
3280                     this.currentFrame = this.totalFrames;
3281                     this._onTween.fire();
3282                 }
3283                 Roo.lib.AnimMgr.stop(this);
3284             };
3285
3286             var onStart = function() {
3287                 this.onStart.fire();
3288
3289                 this.runtimeAttributes = {};
3290                 for (var attr in this.attributes) {
3291                     this.setRuntimeAttribute(attr);
3292                 }
3293
3294                 isAnimated = true;
3295                 actualFrames = 0;
3296                 startTime = new Date();
3297             };
3298
3299
3300             var onTween = function() {
3301                 var data = {
3302                     duration: new Date() - this.getStartTime(),
3303                     currentFrame: this.currentFrame
3304                 };
3305
3306                 data.toString = function() {
3307                     return (
3308                             'duration: ' + data.duration +
3309                             ', currentFrame: ' + data.currentFrame
3310                             );
3311                 };
3312
3313                 this.onTween.fire(data);
3314
3315                 var runtimeAttributes = this.runtimeAttributes;
3316
3317                 for (var attr in runtimeAttributes) {
3318                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3319                 }
3320
3321                 actualFrames += 1;
3322             };
3323
3324             var onComplete = function() {
3325                 var actual_duration = (new Date() - startTime) / 1000 ;
3326
3327                 var data = {
3328                     duration: actual_duration,
3329                     frames: actualFrames,
3330                     fps: actualFrames / actual_duration
3331                 };
3332
3333                 data.toString = function() {
3334                     return (
3335                             'duration: ' + data.duration +
3336                             ', frames: ' + data.frames +
3337                             ', fps: ' + data.fps
3338                             );
3339                 };
3340
3341                 isAnimated = false;
3342                 actualFrames = 0;
3343                 this.onComplete.fire(data);
3344             };
3345
3346
3347             this._onStart = new Roo.util.Event(this);
3348             this.onStart = new Roo.util.Event(this);
3349             this.onTween = new Roo.util.Event(this);
3350             this._onTween = new Roo.util.Event(this);
3351             this.onComplete = new Roo.util.Event(this);
3352             this._onComplete = new Roo.util.Event(this);
3353             this._onStart.addListener(onStart);
3354             this._onTween.addListener(onTween);
3355             this._onComplete.addListener(onComplete);
3356         }
3357     };
3358 })();
3359 /*
3360  * Portions of this file are based on pieces of Yahoo User Interface Library
3361  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3362  * YUI licensed under the BSD License:
3363  * http://developer.yahoo.net/yui/license.txt
3364  * <script type="text/javascript">
3365  *
3366  */
3367
3368 Roo.lib.AnimMgr = new function() {
3369
3370         var thread = null;
3371
3372
3373         var queue = [];
3374
3375
3376         var tweenCount = 0;
3377
3378
3379         this.fps = 1000;
3380
3381
3382         this.delay = 1;
3383
3384
3385         this.registerElement = function(tween) {
3386             queue[queue.length] = tween;
3387             tweenCount += 1;
3388             tween._onStart.fire();
3389             this.start();
3390         };
3391
3392
3393         this.unRegister = function(tween, index) {
3394             tween._onComplete.fire();
3395             index = index || getIndex(tween);
3396             if (index != -1) {
3397                 queue.splice(index, 1);
3398             }
3399
3400             tweenCount -= 1;
3401             if (tweenCount <= 0) {
3402                 this.stop();
3403             }
3404         };
3405
3406
3407         this.start = function() {
3408             if (thread === null) {
3409                 thread = setInterval(this.run, this.delay);
3410             }
3411         };
3412
3413
3414         this.stop = function(tween) {
3415             if (!tween) {
3416                 clearInterval(thread);
3417
3418                 for (var i = 0, len = queue.length; i < len; ++i) {
3419                     if (queue[0].isAnimated()) {
3420                         this.unRegister(queue[0], 0);
3421                     }
3422                 }
3423
3424                 queue = [];
3425                 thread = null;
3426                 tweenCount = 0;
3427             }
3428             else {
3429                 this.unRegister(tween);
3430             }
3431         };
3432
3433
3434         this.run = function() {
3435             for (var i = 0, len = queue.length; i < len; ++i) {
3436                 var tween = queue[i];
3437                 if (!tween || !tween.isAnimated()) {
3438                     continue;
3439                 }
3440
3441                 if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3442                 {
3443                     tween.currentFrame += 1;
3444
3445                     if (tween.useSeconds) {
3446                         correctFrame(tween);
3447                     }
3448                     tween._onTween.fire();
3449                 }
3450                 else {
3451                     Roo.lib.AnimMgr.stop(tween, i);
3452                 }
3453             }
3454         };
3455
3456         var getIndex = function(anim) {
3457             for (var i = 0, len = queue.length; i < len; ++i) {
3458                 if (queue[i] == anim) {
3459                     return i;
3460                 }
3461             }
3462             return -1;
3463         };
3464
3465
3466         var correctFrame = function(tween) {
3467             var frames = tween.totalFrames;
3468             var frame = tween.currentFrame;
3469             var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3470             var elapsed = (new Date() - tween.getStartTime());
3471             var tweak = 0;
3472
3473             if (elapsed < tween.duration * 1000) {
3474                 tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3475             } else {
3476                 tweak = frames - (frame + 1);
3477             }
3478             if (tweak > 0 && isFinite(tweak)) {
3479                 if (tween.currentFrame + tweak >= frames) {
3480                     tweak = frames - (frame + 1);
3481                 }
3482
3483                 tween.currentFrame += tweak;
3484             }
3485         };
3486     };/*
3487  * Portions of this file are based on pieces of Yahoo User Interface Library
3488  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3489  * YUI licensed under the BSD License:
3490  * http://developer.yahoo.net/yui/license.txt
3491  * <script type="text/javascript">
3492  *
3493  */
3494 Roo.lib.Bezier = new function() {
3495
3496         this.getPosition = function(points, t) {
3497             var n = points.length;
3498             var tmp = [];
3499
3500             for (var i = 0; i < n; ++i) {
3501                 tmp[i] = [points[i][0], points[i][1]];
3502             }
3503
3504             for (var j = 1; j < n; ++j) {
3505                 for (i = 0; i < n - j; ++i) {
3506                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3507                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3508                 }
3509             }
3510
3511             return [ tmp[0][0], tmp[0][1] ];
3512
3513         };
3514     };/*
3515  * Portions of this file are based on pieces of Yahoo User Interface Library
3516  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3517  * YUI licensed under the BSD License:
3518  * http://developer.yahoo.net/yui/license.txt
3519  * <script type="text/javascript">
3520  *
3521  */
3522 (function() {
3523
3524     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3525         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3526     };
3527
3528     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3529
3530     var fly = Roo.lib.AnimBase.fly;
3531     var Y = Roo.lib;
3532     var superclass = Y.ColorAnim.superclass;
3533     var proto = Y.ColorAnim.prototype;
3534
3535     proto.toString = function() {
3536         var el = this.getEl();
3537         var id = el.id || el.tagName;
3538         return ("ColorAnim " + id);
3539     };
3540
3541     proto.patterns.color = /color$/i;
3542     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3543     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3544     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3545     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3546
3547
3548     proto.parseColor = function(s) {
3549         if (s.length == 3) {
3550             return s;
3551         }
3552
3553         var c = this.patterns.hex.exec(s);
3554         if (c && c.length == 4) {
3555             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3556         }
3557
3558         c = this.patterns.rgb.exec(s);
3559         if (c && c.length == 4) {
3560             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3561         }
3562
3563         c = this.patterns.hex3.exec(s);
3564         if (c && c.length == 4) {
3565             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3566         }
3567
3568         return null;
3569     };
3570     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3571     proto.getAttribute = function(attr) {
3572         var el = this.getEl();
3573         if (this.patterns.color.test(attr)) {
3574             var val = fly(el).getStyle(attr);
3575
3576             if (this.patterns.transparent.test(val)) {
3577                 var parent = el.parentNode;
3578                 val = fly(parent).getStyle(attr);
3579
3580                 while (parent && this.patterns.transparent.test(val)) {
3581                     parent = parent.parentNode;
3582                     val = fly(parent).getStyle(attr);
3583                     if (parent.tagName.toUpperCase() == 'HTML') {
3584                         val = '#fff';
3585                     }
3586                 }
3587             }
3588         } else {
3589             val = superclass.getAttribute.call(this, attr);
3590         }
3591
3592         return val;
3593     };
3594     proto.getAttribute = function(attr) {
3595         var el = this.getEl();
3596         if (this.patterns.color.test(attr)) {
3597             var val = fly(el).getStyle(attr);
3598
3599             if (this.patterns.transparent.test(val)) {
3600                 var parent = el.parentNode;
3601                 val = fly(parent).getStyle(attr);
3602
3603                 while (parent && this.patterns.transparent.test(val)) {
3604                     parent = parent.parentNode;
3605                     val = fly(parent).getStyle(attr);
3606                     if (parent.tagName.toUpperCase() == 'HTML') {
3607                         val = '#fff';
3608                     }
3609                 }
3610             }
3611         } else {
3612             val = superclass.getAttribute.call(this, attr);
3613         }
3614
3615         return val;
3616     };
3617
3618     proto.doMethod = function(attr, start, end) {
3619         var val;
3620
3621         if (this.patterns.color.test(attr)) {
3622             val = [];
3623             for (var i = 0, len = start.length; i < len; ++i) {
3624                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3625             }
3626
3627             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3628         }
3629         else {
3630             val = superclass.doMethod.call(this, attr, start, end);
3631         }
3632
3633         return val;
3634     };
3635
3636     proto.setRuntimeAttribute = function(attr) {
3637         superclass.setRuntimeAttribute.call(this, attr);
3638
3639         if (this.patterns.color.test(attr)) {
3640             var attributes = this.attributes;
3641             var start = this.parseColor(this.runtimeAttributes[attr].start);
3642             var end = this.parseColor(this.runtimeAttributes[attr].end);
3643
3644             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3645                 end = this.parseColor(attributes[attr].by);
3646
3647                 for (var i = 0, len = start.length; i < len; ++i) {
3648                     end[i] = start[i] + end[i];
3649                 }
3650             }
3651
3652             this.runtimeAttributes[attr].start = start;
3653             this.runtimeAttributes[attr].end = end;
3654         }
3655     };
3656 })();
3657
3658 /*
3659  * Portions of this file are based on pieces of Yahoo User Interface Library
3660  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3661  * YUI licensed under the BSD License:
3662  * http://developer.yahoo.net/yui/license.txt
3663  * <script type="text/javascript">
3664  *
3665  */
3666 Roo.lib.Easing = {
3667
3668
3669     easeNone: function (t, b, c, d) {
3670         return c * t / d + b;
3671     },
3672
3673
3674     easeIn: function (t, b, c, d) {
3675         return c * (t /= d) * t + b;
3676     },
3677
3678
3679     easeOut: function (t, b, c, d) {
3680         return -c * (t /= d) * (t - 2) + b;
3681     },
3682
3683
3684     easeBoth: function (t, b, c, d) {
3685         if ((t /= d / 2) < 1) {
3686             return c / 2 * t * t + b;
3687         }
3688
3689         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3690     },
3691
3692
3693     easeInStrong: function (t, b, c, d) {
3694         return c * (t /= d) * t * t * t + b;
3695     },
3696
3697
3698     easeOutStrong: function (t, b, c, d) {
3699         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3700     },
3701
3702
3703     easeBothStrong: function (t, b, c, d) {
3704         if ((t /= d / 2) < 1) {
3705             return c / 2 * t * t * t * t + b;
3706         }
3707
3708         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3709     },
3710
3711
3712
3713     elasticIn: function (t, b, c, d, a, p) {
3714         if (t == 0) {
3715             return b;
3716         }
3717         if ((t /= d) == 1) {
3718             return b + c;
3719         }
3720         if (!p) {
3721             p = d * .3;
3722         }
3723
3724         if (!a || a < Math.abs(c)) {
3725             a = c;
3726             var s = p / 4;
3727         }
3728         else {
3729             var s = p / (2 * Math.PI) * Math.asin(c / a);
3730         }
3731
3732         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3733     },
3734
3735
3736     elasticOut: function (t, b, c, d, a, p) {
3737         if (t == 0) {
3738             return b;
3739         }
3740         if ((t /= d) == 1) {
3741             return b + c;
3742         }
3743         if (!p) {
3744             p = d * .3;
3745         }
3746
3747         if (!a || a < Math.abs(c)) {
3748             a = c;
3749             var s = p / 4;
3750         }
3751         else {
3752             var s = p / (2 * Math.PI) * Math.asin(c / a);
3753         }
3754
3755         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3756     },
3757
3758
3759     elasticBoth: function (t, b, c, d, a, p) {
3760         if (t == 0) {
3761             return b;
3762         }
3763
3764         if ((t /= d / 2) == 2) {
3765             return b + c;
3766         }
3767
3768         if (!p) {
3769             p = d * (.3 * 1.5);
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         if (t < 1) {
3781             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3782                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3783         }
3784         return a * Math.pow(2, -10 * (t -= 1)) *
3785                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3786     },
3787
3788
3789
3790     backIn: function (t, b, c, d, s) {
3791         if (typeof s == 'undefined') {
3792             s = 1.70158;
3793         }
3794         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3795     },
3796
3797
3798     backOut: function (t, b, c, d, s) {
3799         if (typeof s == 'undefined') {
3800             s = 1.70158;
3801         }
3802         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3803     },
3804
3805
3806     backBoth: function (t, b, c, d, s) {
3807         if (typeof s == 'undefined') {
3808             s = 1.70158;
3809         }
3810
3811         if ((t /= d / 2 ) < 1) {
3812             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3813         }
3814         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3815     },
3816
3817
3818     bounceIn: function (t, b, c, d) {
3819         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3820     },
3821
3822
3823     bounceOut: function (t, b, c, d) {
3824         if ((t /= d) < (1 / 2.75)) {
3825             return c * (7.5625 * t * t) + b;
3826         } else if (t < (2 / 2.75)) {
3827             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3828         } else if (t < (2.5 / 2.75)) {
3829             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3830         }
3831         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3832     },
3833
3834
3835     bounceBoth: function (t, b, c, d) {
3836         if (t < d / 2) {
3837             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3838         }
3839         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3840     }
3841 };/*
3842  * Portions of this file are based on pieces of Yahoo User Interface Library
3843  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3844  * YUI licensed under the BSD License:
3845  * http://developer.yahoo.net/yui/license.txt
3846  * <script type="text/javascript">
3847  *
3848  */
3849     (function() {
3850         Roo.lib.Motion = function(el, attributes, duration, method) {
3851             if (el) {
3852                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3853             }
3854         };
3855
3856         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3857
3858
3859         var Y = Roo.lib;
3860         var superclass = Y.Motion.superclass;
3861         var proto = Y.Motion.prototype;
3862
3863         proto.toString = function() {
3864             var el = this.getEl();
3865             var id = el.id || el.tagName;
3866             return ("Motion " + id);
3867         };
3868
3869         proto.patterns.points = /^points$/i;
3870
3871         proto.setAttribute = function(attr, val, unit) {
3872             if (this.patterns.points.test(attr)) {
3873                 unit = unit || 'px';
3874                 superclass.setAttribute.call(this, 'left', val[0], unit);
3875                 superclass.setAttribute.call(this, 'top', val[1], unit);
3876             } else {
3877                 superclass.setAttribute.call(this, attr, val, unit);
3878             }
3879         };
3880
3881         proto.getAttribute = function(attr) {
3882             if (this.patterns.points.test(attr)) {
3883                 var val = [
3884                         superclass.getAttribute.call(this, 'left'),
3885                         superclass.getAttribute.call(this, 'top')
3886                         ];
3887             } else {
3888                 val = superclass.getAttribute.call(this, attr);
3889             }
3890
3891             return val;
3892         };
3893
3894         proto.doMethod = function(attr, start, end) {
3895             var val = null;
3896
3897             if (this.patterns.points.test(attr)) {
3898                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
3899                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
3900             } else {
3901                 val = superclass.doMethod.call(this, attr, start, end);
3902             }
3903             return val;
3904         };
3905
3906         proto.setRuntimeAttribute = function(attr) {
3907             if (this.patterns.points.test(attr)) {
3908                 var el = this.getEl();
3909                 var attributes = this.attributes;
3910                 var start;
3911                 var control = attributes['points']['control'] || [];
3912                 var end;
3913                 var i, len;
3914
3915                 if (control.length > 0 && !(control[0] instanceof Array)) {
3916                     control = [control];
3917                 } else {
3918                     var tmp = [];
3919                     for (i = 0,len = control.length; i < len; ++i) {
3920                         tmp[i] = control[i];
3921                     }
3922                     control = tmp;
3923                 }
3924
3925                 Roo.fly(el).position();
3926
3927                 if (isset(attributes['points']['from'])) {
3928                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
3929                 }
3930                 else {
3931                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
3932                 }
3933
3934                 start = this.getAttribute('points');
3935
3936
3937                 if (isset(attributes['points']['to'])) {
3938                     end = translateValues.call(this, attributes['points']['to'], start);
3939
3940                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
3941                     for (i = 0,len = control.length; i < len; ++i) {
3942                         control[i] = translateValues.call(this, control[i], start);
3943                     }
3944
3945
3946                 } else if (isset(attributes['points']['by'])) {
3947                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
3948
3949                     for (i = 0,len = control.length; i < len; ++i) {
3950                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
3951                     }
3952                 }
3953
3954                 this.runtimeAttributes[attr] = [start];
3955
3956                 if (control.length > 0) {
3957                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
3958                 }
3959
3960                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
3961             }
3962             else {
3963                 superclass.setRuntimeAttribute.call(this, attr);
3964             }
3965         };
3966
3967         var translateValues = function(val, start) {
3968             var pageXY = Roo.lib.Dom.getXY(this.getEl());
3969             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
3970
3971             return val;
3972         };
3973
3974         var isset = function(prop) {
3975             return (typeof prop !== 'undefined');
3976         };
3977     })();
3978 /*
3979  * Portions of this file are based on pieces of Yahoo User Interface Library
3980  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3981  * YUI licensed under the BSD License:
3982  * http://developer.yahoo.net/yui/license.txt
3983  * <script type="text/javascript">
3984  *
3985  */
3986     (function() {
3987         Roo.lib.Scroll = function(el, attributes, duration, method) {
3988             if (el) {
3989                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
3990             }
3991         };
3992
3993         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
3994
3995
3996         var Y = Roo.lib;
3997         var superclass = Y.Scroll.superclass;
3998         var proto = Y.Scroll.prototype;
3999
4000         proto.toString = function() {
4001             var el = this.getEl();
4002             var id = el.id || el.tagName;
4003             return ("Scroll " + id);
4004         };
4005
4006         proto.doMethod = function(attr, start, end) {
4007             var val = null;
4008
4009             if (attr == 'scroll') {
4010                 val = [
4011                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4012                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4013                         ];
4014
4015             } else {
4016                 val = superclass.doMethod.call(this, attr, start, end);
4017             }
4018             return val;
4019         };
4020
4021         proto.getAttribute = function(attr) {
4022             var val = null;
4023             var el = this.getEl();
4024
4025             if (attr == 'scroll') {
4026                 val = [ el.scrollLeft, el.scrollTop ];
4027             } else {
4028                 val = superclass.getAttribute.call(this, attr);
4029             }
4030
4031             return val;
4032         };
4033
4034         proto.setAttribute = function(attr, val, unit) {
4035             var el = this.getEl();
4036
4037             if (attr == 'scroll') {
4038                 el.scrollLeft = val[0];
4039                 el.scrollTop = val[1];
4040             } else {
4041                 superclass.setAttribute.call(this, attr, val, unit);
4042             }
4043         };
4044     })();
4045 /*
4046  * Based on:
4047  * Ext JS Library 1.1.1
4048  * Copyright(c) 2006-2007, Ext JS, LLC.
4049  *
4050  * Originally Released Under LGPL - original licence link has changed is not relivant.
4051  *
4052  * Fork - LGPL
4053  * <script type="text/javascript">
4054  */
4055
4056
4057 // nasty IE9 hack - what a pile of crap that is..
4058
4059  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4060     Range.prototype.createContextualFragment = function (html) {
4061         var doc = window.document;
4062         var container = doc.createElement("div");
4063         container.innerHTML = html;
4064         var frag = doc.createDocumentFragment(), n;
4065         while ((n = container.firstChild)) {
4066             frag.appendChild(n);
4067         }
4068         return frag;
4069     };
4070 }
4071
4072 /**
4073  * @class Roo.DomHelper
4074  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4075  * 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>.
4076  * @singleton
4077  */
4078 Roo.DomHelper = function(){
4079     var tempTableEl = null;
4080     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4081     var tableRe = /^table|tbody|tr|td$/i;
4082     var xmlns = {};
4083     // build as innerHTML where available
4084     /** @ignore */
4085     var createHtml = function(o){
4086         if(typeof o == 'string'){
4087             return o;
4088         }
4089         var b = "";
4090         if(!o.tag){
4091             o.tag = "div";
4092         }
4093         b += "<" + o.tag;
4094         for(var attr in o){
4095             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") continue;
4096             if(attr == "style"){
4097                 var s = o["style"];
4098                 if(typeof s == "function"){
4099                     s = s.call();
4100                 }
4101                 if(typeof s == "string"){
4102                     b += ' style="' + s + '"';
4103                 }else if(typeof s == "object"){
4104                     b += ' style="';
4105                     for(var key in s){
4106                         if(typeof s[key] != "function"){
4107                             b += key + ":" + s[key] + ";";
4108                         }
4109                     }
4110                     b += '"';
4111                 }
4112             }else{
4113                 if(attr == "cls"){
4114                     b += ' class="' + o["cls"] + '"';
4115                 }else if(attr == "htmlFor"){
4116                     b += ' for="' + o["htmlFor"] + '"';
4117                 }else{
4118                     b += " " + attr + '="' + o[attr] + '"';
4119                 }
4120             }
4121         }
4122         if(emptyTags.test(o.tag)){
4123             b += "/>";
4124         }else{
4125             b += ">";
4126             var cn = o.children || o.cn;
4127             if(cn){
4128                 //http://bugs.kde.org/show_bug.cgi?id=71506
4129                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4130                     for(var i = 0, len = cn.length; i < len; i++) {
4131                         b += createHtml(cn[i], b);
4132                     }
4133                 }else{
4134                     b += createHtml(cn, b);
4135                 }
4136             }
4137             if(o.html){
4138                 b += o.html;
4139             }
4140             b += "</" + o.tag + ">";
4141         }
4142         return b;
4143     };
4144
4145     // build as dom
4146     /** @ignore */
4147     var createDom = function(o, parentNode){
4148          
4149         // defininition craeted..
4150         var ns = false;
4151         if (o.ns && o.ns != 'html') {
4152                
4153             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4154                 xmlns[o.ns] = o.xmlns;
4155                 ns = o.xmlns;
4156             }
4157             if (typeof(xmlns[o.ns]) == 'undefined') {
4158                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4159             }
4160             ns = xmlns[o.ns];
4161         }
4162         
4163         
4164         if (typeof(o) == 'string') {
4165             return parentNode.appendChild(document.createTextNode(o));
4166         }
4167         o.tag = o.tag || div;
4168         if (o.ns && Roo.isIE) {
4169             ns = false;
4170             o.tag = o.ns + ':' + o.tag;
4171             
4172         }
4173         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4174         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4175         for(var attr in o){
4176             
4177             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4178                     attr == "style" || typeof o[attr] == "function") continue;
4179                     
4180             if(attr=="cls" && Roo.isIE){
4181                 el.className = o["cls"];
4182             }else{
4183                 if(useSet) el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);
4184                 else el[attr] = o[attr];
4185             }
4186         }
4187         Roo.DomHelper.applyStyles(el, o.style);
4188         var cn = o.children || o.cn;
4189         if(cn){
4190             //http://bugs.kde.org/show_bug.cgi?id=71506
4191              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4192                 for(var i = 0, len = cn.length; i < len; i++) {
4193                     createDom(cn[i], el);
4194                 }
4195             }else{
4196                 createDom(cn, el);
4197             }
4198         }
4199         if(o.html){
4200             el.innerHTML = o.html;
4201         }
4202         if(parentNode){
4203            parentNode.appendChild(el);
4204         }
4205         return el;
4206     };
4207
4208     var ieTable = function(depth, s, h, e){
4209         tempTableEl.innerHTML = [s, h, e].join('');
4210         var i = -1, el = tempTableEl;
4211         while(++i < depth){
4212             el = el.firstChild;
4213         }
4214         return el;
4215     };
4216
4217     // kill repeat to save bytes
4218     var ts = '<table>',
4219         te = '</table>',
4220         tbs = ts+'<tbody>',
4221         tbe = '</tbody>'+te,
4222         trs = tbs + '<tr>',
4223         tre = '</tr>'+tbe;
4224
4225     /**
4226      * @ignore
4227      * Nasty code for IE's broken table implementation
4228      */
4229     var insertIntoTable = function(tag, where, el, html){
4230         if(!tempTableEl){
4231             tempTableEl = document.createElement('div');
4232         }
4233         var node;
4234         var before = null;
4235         if(tag == 'td'){
4236             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4237                 return;
4238             }
4239             if(where == 'beforebegin'){
4240                 before = el;
4241                 el = el.parentNode;
4242             } else{
4243                 before = el.nextSibling;
4244                 el = el.parentNode;
4245             }
4246             node = ieTable(4, trs, html, tre);
4247         }
4248         else if(tag == 'tr'){
4249             if(where == 'beforebegin'){
4250                 before = el;
4251                 el = el.parentNode;
4252                 node = ieTable(3, tbs, html, tbe);
4253             } else if(where == 'afterend'){
4254                 before = el.nextSibling;
4255                 el = el.parentNode;
4256                 node = ieTable(3, tbs, html, tbe);
4257             } else{ // INTO a TR
4258                 if(where == 'afterbegin'){
4259                     before = el.firstChild;
4260                 }
4261                 node = ieTable(4, trs, html, tre);
4262             }
4263         } else if(tag == 'tbody'){
4264             if(where == 'beforebegin'){
4265                 before = el;
4266                 el = el.parentNode;
4267                 node = ieTable(2, ts, html, te);
4268             } else if(where == 'afterend'){
4269                 before = el.nextSibling;
4270                 el = el.parentNode;
4271                 node = ieTable(2, ts, html, te);
4272             } else{
4273                 if(where == 'afterbegin'){
4274                     before = el.firstChild;
4275                 }
4276                 node = ieTable(3, tbs, html, tbe);
4277             }
4278         } else{ // TABLE
4279             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4280                 return;
4281             }
4282             if(where == 'afterbegin'){
4283                 before = el.firstChild;
4284             }
4285             node = ieTable(2, ts, html, te);
4286         }
4287         el.insertBefore(node, before);
4288         return node;
4289     };
4290
4291     return {
4292     /** True to force the use of DOM instead of html fragments @type Boolean */
4293     useDom : false,
4294
4295     /**
4296      * Returns the markup for the passed Element(s) config
4297      * @param {Object} o The Dom object spec (and children)
4298      * @return {String}
4299      */
4300     markup : function(o){
4301         return createHtml(o);
4302     },
4303
4304     /**
4305      * Applies a style specification to an element
4306      * @param {String/HTMLElement} el The element to apply styles to
4307      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4308      * a function which returns such a specification.
4309      */
4310     applyStyles : function(el, styles){
4311         if(styles){
4312            el = Roo.fly(el);
4313            if(typeof styles == "string"){
4314                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4315                var matches;
4316                while ((matches = re.exec(styles)) != null){
4317                    el.setStyle(matches[1], matches[2]);
4318                }
4319            }else if (typeof styles == "object"){
4320                for (var style in styles){
4321                   el.setStyle(style, styles[style]);
4322                }
4323            }else if (typeof styles == "function"){
4324                 Roo.DomHelper.applyStyles(el, styles.call());
4325            }
4326         }
4327     },
4328
4329     /**
4330      * Inserts an HTML fragment into the Dom
4331      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4332      * @param {HTMLElement} el The context element
4333      * @param {String} html The HTML fragmenet
4334      * @return {HTMLElement} The new node
4335      */
4336     insertHtml : function(where, el, html){
4337         where = where.toLowerCase();
4338         if(el.insertAdjacentHTML){
4339             if(tableRe.test(el.tagName)){
4340                 var rs;
4341                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4342                     return rs;
4343                 }
4344             }
4345             switch(where){
4346                 case "beforebegin":
4347                     el.insertAdjacentHTML('BeforeBegin', html);
4348                     return el.previousSibling;
4349                 case "afterbegin":
4350                     el.insertAdjacentHTML('AfterBegin', html);
4351                     return el.firstChild;
4352                 case "beforeend":
4353                     el.insertAdjacentHTML('BeforeEnd', html);
4354                     return el.lastChild;
4355                 case "afterend":
4356                     el.insertAdjacentHTML('AfterEnd', html);
4357                     return el.nextSibling;
4358             }
4359             throw 'Illegal insertion point -> "' + where + '"';
4360         }
4361         var range = el.ownerDocument.createRange();
4362         var frag;
4363         switch(where){
4364              case "beforebegin":
4365                 range.setStartBefore(el);
4366                 frag = range.createContextualFragment(html);
4367                 el.parentNode.insertBefore(frag, el);
4368                 return el.previousSibling;
4369              case "afterbegin":
4370                 if(el.firstChild){
4371                     range.setStartBefore(el.firstChild);
4372                     frag = range.createContextualFragment(html);
4373                     el.insertBefore(frag, el.firstChild);
4374                     return el.firstChild;
4375                 }else{
4376                     el.innerHTML = html;
4377                     return el.firstChild;
4378                 }
4379             case "beforeend":
4380                 if(el.lastChild){
4381                     range.setStartAfter(el.lastChild);
4382                     frag = range.createContextualFragment(html);
4383                     el.appendChild(frag);
4384                     return el.lastChild;
4385                 }else{
4386                     el.innerHTML = html;
4387                     return el.lastChild;
4388                 }
4389             case "afterend":
4390                 range.setStartAfter(el);
4391                 frag = range.createContextualFragment(html);
4392                 el.parentNode.insertBefore(frag, el.nextSibling);
4393                 return el.nextSibling;
4394             }
4395             throw 'Illegal insertion point -> "' + where + '"';
4396     },
4397
4398     /**
4399      * Creates new Dom element(s) and inserts them before el
4400      * @param {String/HTMLElement/Element} el The context element
4401      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4402      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4403      * @return {HTMLElement/Roo.Element} The new node
4404      */
4405     insertBefore : function(el, o, returnElement){
4406         return this.doInsert(el, o, returnElement, "beforeBegin");
4407     },
4408
4409     /**
4410      * Creates new Dom element(s) and inserts them after el
4411      * @param {String/HTMLElement/Element} el The context element
4412      * @param {Object} o The Dom object spec (and children)
4413      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4414      * @return {HTMLElement/Roo.Element} The new node
4415      */
4416     insertAfter : function(el, o, returnElement){
4417         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4418     },
4419
4420     /**
4421      * Creates new Dom element(s) and inserts them as the first child of el
4422      * @param {String/HTMLElement/Element} el The context element
4423      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4424      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4425      * @return {HTMLElement/Roo.Element} The new node
4426      */
4427     insertFirst : function(el, o, returnElement){
4428         return this.doInsert(el, o, returnElement, "afterBegin");
4429     },
4430
4431     // private
4432     doInsert : function(el, o, returnElement, pos, sibling){
4433         el = Roo.getDom(el);
4434         var newNode;
4435         if(this.useDom || o.ns){
4436             newNode = createDom(o, null);
4437             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4438         }else{
4439             var html = createHtml(o);
4440             newNode = this.insertHtml(pos, el, html);
4441         }
4442         return returnElement ? Roo.get(newNode, true) : newNode;
4443     },
4444
4445     /**
4446      * Creates new Dom element(s) and appends them to 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     append : function(el, o, returnElement){
4453         el = Roo.getDom(el);
4454         var newNode;
4455         if(this.useDom || o.ns){
4456             newNode = createDom(o, null);
4457             el.appendChild(newNode);
4458         }else{
4459             var html = createHtml(o);
4460             newNode = this.insertHtml("beforeEnd", el, html);
4461         }
4462         return returnElement ? Roo.get(newNode, true) : newNode;
4463     },
4464
4465     /**
4466      * Creates new Dom element(s) and overwrites the contents of el with them
4467      * @param {String/HTMLElement/Element} el The context element
4468      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4469      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4470      * @return {HTMLElement/Roo.Element} The new node
4471      */
4472     overwrite : function(el, o, returnElement){
4473         el = Roo.getDom(el);
4474         if (o.ns) {
4475           
4476             while (el.childNodes.length) {
4477                 el.removeChild(el.firstChild);
4478             }
4479             createDom(o, el);
4480         } else {
4481             el.innerHTML = createHtml(o);   
4482         }
4483         
4484         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4485     },
4486
4487     /**
4488      * Creates a new Roo.DomHelper.Template from the Dom object spec
4489      * @param {Object} o The Dom object spec (and children)
4490      * @return {Roo.DomHelper.Template} The new template
4491      */
4492     createTemplate : function(o){
4493         var html = createHtml(o);
4494         return new Roo.Template(html);
4495     }
4496     };
4497 }();
4498 /*
4499  * Based on:
4500  * Ext JS Library 1.1.1
4501  * Copyright(c) 2006-2007, Ext JS, LLC.
4502  *
4503  * Originally Released Under LGPL - original licence link has changed is not relivant.
4504  *
4505  * Fork - LGPL
4506  * <script type="text/javascript">
4507  */
4508  
4509 /**
4510 * @class Roo.Template
4511 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4512 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4513 * Usage:
4514 <pre><code>
4515 var t = new Roo.Template({
4516     html :  '&lt;div name="{id}"&gt;' + 
4517         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4518         '&lt;/div&gt;',
4519     myformat: function (value, allValues) {
4520         return 'XX' + value;
4521     }
4522 });
4523 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4524 </code></pre>
4525 * For more information see this blog post with examples: <a href="http://www.jackslocum.com/yui/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">DomHelper - Create Elements using DOM, HTML fragments and Templates</a>. 
4526 * @constructor
4527 * @param {Object} cfg - Configuration object.
4528 */
4529 Roo.Template = function(cfg){
4530     // BC!
4531     if(cfg instanceof Array){
4532         cfg = cfg.join("");
4533     }else if(arguments.length > 1){
4534         cfg = Array.prototype.join.call(arguments, "");
4535     }
4536     
4537     
4538     if (typeof(cfg) == 'object') {
4539         Roo.apply(this,cfg)
4540     } else {
4541         // bc
4542         this.html = cfg;
4543     }
4544     
4545     
4546 };
4547 Roo.Template.prototype = {
4548     
4549     /**
4550      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4551      */
4552     html : '',
4553     /**
4554      * Returns an HTML fragment of this template with the specified values applied.
4555      * @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'})
4556      * @return {String} The HTML fragment
4557      */
4558     applyTemplate : function(values){
4559         try {
4560             
4561             if(this.compiled){
4562                 return this.compiled(values);
4563             }
4564             var useF = this.disableFormats !== true;
4565             var fm = Roo.util.Format, tpl = this;
4566             var fn = function(m, name, format, args){
4567                 if(format && useF){
4568                     if(format.substr(0, 5) == "this."){
4569                         return tpl.call(format.substr(5), values[name], values);
4570                     }else{
4571                         if(args){
4572                             // quoted values are required for strings in compiled templates, 
4573                             // but for non compiled we need to strip them
4574                             // quoted reversed for jsmin
4575                             var re = /^\s*['"](.*)["']\s*$/;
4576                             args = args.split(',');
4577                             for(var i = 0, len = args.length; i < len; i++){
4578                                 args[i] = args[i].replace(re, "$1");
4579                             }
4580                             args = [values[name]].concat(args);
4581                         }else{
4582                             args = [values[name]];
4583                         }
4584                         return fm[format].apply(fm, args);
4585                     }
4586                 }else{
4587                     return values[name] !== undefined ? values[name] : "";
4588                 }
4589             };
4590             return this.html.replace(this.re, fn);
4591         } catch (e) {
4592             Roo.log(e);
4593             throw e;
4594         }
4595          
4596     },
4597     
4598     /**
4599      * Sets the HTML used as the template and optionally compiles it.
4600      * @param {String} html
4601      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4602      * @return {Roo.Template} this
4603      */
4604     set : function(html, compile){
4605         this.html = html;
4606         this.compiled = null;
4607         if(compile){
4608             this.compile();
4609         }
4610         return this;
4611     },
4612     
4613     /**
4614      * True to disable format functions (defaults to false)
4615      * @type Boolean
4616      */
4617     disableFormats : false,
4618     
4619     /**
4620     * The regular expression used to match template variables 
4621     * @type RegExp
4622     * @property 
4623     */
4624     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4625     
4626     /**
4627      * Compiles the template into an internal function, eliminating the RegEx overhead.
4628      * @return {Roo.Template} this
4629      */
4630     compile : function(){
4631         var fm = Roo.util.Format;
4632         var useF = this.disableFormats !== true;
4633         var sep = Roo.isGecko ? "+" : ",";
4634         var fn = function(m, name, format, args){
4635             if(format && useF){
4636                 args = args ? ',' + args : "";
4637                 if(format.substr(0, 5) != "this."){
4638                     format = "fm." + format + '(';
4639                 }else{
4640                     format = 'this.call("'+ format.substr(5) + '", ';
4641                     args = ", values";
4642                 }
4643             }else{
4644                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4645             }
4646             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4647         };
4648         var body;
4649         // branched to use + in gecko and [].join() in others
4650         if(Roo.isGecko){
4651             body = "this.compiled = function(values){ return '" +
4652                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4653                     "';};";
4654         }else{
4655             body = ["this.compiled = function(values){ return ['"];
4656             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4657             body.push("'].join('');};");
4658             body = body.join('');
4659         }
4660         /**
4661          * eval:var:values
4662          * eval:var:fm
4663          */
4664         eval(body);
4665         return this;
4666     },
4667     
4668     // private function used to call members
4669     call : function(fnName, value, allValues){
4670         return this[fnName](value, allValues);
4671     },
4672     
4673     /**
4674      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4675      * @param {String/HTMLElement/Roo.Element} el The context element
4676      * @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'})
4677      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4678      * @return {HTMLElement/Roo.Element} The new node or Element
4679      */
4680     insertFirst: function(el, values, returnElement){
4681         return this.doInsert('afterBegin', el, values, returnElement);
4682     },
4683
4684     /**
4685      * Applies the supplied values to the template and inserts the new node(s) before el.
4686      * @param {String/HTMLElement/Roo.Element} el The context element
4687      * @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'})
4688      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4689      * @return {HTMLElement/Roo.Element} The new node or Element
4690      */
4691     insertBefore: function(el, values, returnElement){
4692         return this.doInsert('beforeBegin', el, values, returnElement);
4693     },
4694
4695     /**
4696      * Applies the supplied values to the template and inserts the new node(s) after el.
4697      * @param {String/HTMLElement/Roo.Element} el The context element
4698      * @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'})
4699      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4700      * @return {HTMLElement/Roo.Element} The new node or Element
4701      */
4702     insertAfter : function(el, values, returnElement){
4703         return this.doInsert('afterEnd', el, values, returnElement);
4704     },
4705     
4706     /**
4707      * Applies the supplied values to the template and appends the new node(s) to el.
4708      * @param {String/HTMLElement/Roo.Element} el The context element
4709      * @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'})
4710      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4711      * @return {HTMLElement/Roo.Element} The new node or Element
4712      */
4713     append : function(el, values, returnElement){
4714         return this.doInsert('beforeEnd', el, values, returnElement);
4715     },
4716
4717     doInsert : function(where, el, values, returnEl){
4718         el = Roo.getDom(el);
4719         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4720         return returnEl ? Roo.get(newNode, true) : newNode;
4721     },
4722
4723     /**
4724      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4725      * @param {String/HTMLElement/Roo.Element} el The context element
4726      * @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'})
4727      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4728      * @return {HTMLElement/Roo.Element} The new node or Element
4729      */
4730     overwrite : function(el, values, returnElement){
4731         el = Roo.getDom(el);
4732         el.innerHTML = this.applyTemplate(values);
4733         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4734     }
4735 };
4736 /**
4737  * Alias for {@link #applyTemplate}
4738  * @method
4739  */
4740 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4741
4742 // backwards compat
4743 Roo.DomHelper.Template = Roo.Template;
4744
4745 /**
4746  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4747  * @param {String/HTMLElement} el A DOM element or its id
4748  * @returns {Roo.Template} The created template
4749  * @static
4750  */
4751 Roo.Template.from = function(el){
4752     el = Roo.getDom(el);
4753     return new Roo.Template(el.value || el.innerHTML);
4754 };/*
4755  * Based on:
4756  * Ext JS Library 1.1.1
4757  * Copyright(c) 2006-2007, Ext JS, LLC.
4758  *
4759  * Originally Released Under LGPL - original licence link has changed is not relivant.
4760  *
4761  * Fork - LGPL
4762  * <script type="text/javascript">
4763  */
4764  
4765
4766 /*
4767  * This is code is also distributed under MIT license for use
4768  * with jQuery and prototype JavaScript libraries.
4769  */
4770 /**
4771  * @class Roo.DomQuery
4772 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).
4773 <p>
4774 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>
4775
4776 <p>
4777 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.
4778 </p>
4779 <h4>Element Selectors:</h4>
4780 <ul class="list">
4781     <li> <b>*</b> any element</li>
4782     <li> <b>E</b> an element with the tag E</li>
4783     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4784     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4785     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4786     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4787 </ul>
4788 <h4>Attribute Selectors:</h4>
4789 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4790 <ul class="list">
4791     <li> <b>E[foo]</b> has an attribute "foo"</li>
4792     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4793     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4794     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4795     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4796     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4797     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4798 </ul>
4799 <h4>Pseudo Classes:</h4>
4800 <ul class="list">
4801     <li> <b>E:first-child</b> E is the first child of its parent</li>
4802     <li> <b>E:last-child</b> E is the last child of its parent</li>
4803     <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>
4804     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4805     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4806     <li> <b>E:only-child</b> E is the only child of its parent</li>
4807     <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>
4808     <li> <b>E:first</b> the first E in the resultset</li>
4809     <li> <b>E:last</b> the last E in the resultset</li>
4810     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4811     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4812     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4813     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4814     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4815     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4816     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4817     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4818     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4819 </ul>
4820 <h4>CSS Value Selectors:</h4>
4821 <ul class="list">
4822     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4823     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4824     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4825     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4826     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4827     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4828 </ul>
4829  * @singleton
4830  */
4831 Roo.DomQuery = function(){
4832     var cache = {}, simpleCache = {}, valueCache = {};
4833     var nonSpace = /\S/;
4834     var trimRe = /^\s+|\s+$/g;
4835     var tplRe = /\{(\d+)\}/g;
4836     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4837     var tagTokenRe = /^(#)?([\w-\*]+)/;
4838     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4839
4840     function child(p, index){
4841         var i = 0;
4842         var n = p.firstChild;
4843         while(n){
4844             if(n.nodeType == 1){
4845                if(++i == index){
4846                    return n;
4847                }
4848             }
4849             n = n.nextSibling;
4850         }
4851         return null;
4852     };
4853
4854     function next(n){
4855         while((n = n.nextSibling) && n.nodeType != 1);
4856         return n;
4857     };
4858
4859     function prev(n){
4860         while((n = n.previousSibling) && n.nodeType != 1);
4861         return n;
4862     };
4863
4864     function children(d){
4865         var n = d.firstChild, ni = -1;
4866             while(n){
4867                 var nx = n.nextSibling;
4868                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
4869                     d.removeChild(n);
4870                 }else{
4871                     n.nodeIndex = ++ni;
4872                 }
4873                 n = nx;
4874             }
4875             return this;
4876         };
4877
4878     function byClassName(c, a, v){
4879         if(!v){
4880             return c;
4881         }
4882         var r = [], ri = -1, cn;
4883         for(var i = 0, ci; ci = c[i]; i++){
4884             if((' '+ci.className+' ').indexOf(v) != -1){
4885                 r[++ri] = ci;
4886             }
4887         }
4888         return r;
4889     };
4890
4891     function attrValue(n, attr){
4892         if(!n.tagName && typeof n.length != "undefined"){
4893             n = n[0];
4894         }
4895         if(!n){
4896             return null;
4897         }
4898         if(attr == "for"){
4899             return n.htmlFor;
4900         }
4901         if(attr == "class" || attr == "className"){
4902             return n.className;
4903         }
4904         return n.getAttribute(attr) || n[attr];
4905
4906     };
4907
4908     function getNodes(ns, mode, tagName){
4909         var result = [], ri = -1, cs;
4910         if(!ns){
4911             return result;
4912         }
4913         tagName = tagName || "*";
4914         if(typeof ns.getElementsByTagName != "undefined"){
4915             ns = [ns];
4916         }
4917         if(!mode){
4918             for(var i = 0, ni; ni = ns[i]; i++){
4919                 cs = ni.getElementsByTagName(tagName);
4920                 for(var j = 0, ci; ci = cs[j]; j++){
4921                     result[++ri] = ci;
4922                 }
4923             }
4924         }else if(mode == "/" || mode == ">"){
4925             var utag = tagName.toUpperCase();
4926             for(var i = 0, ni, cn; ni = ns[i]; i++){
4927                 cn = ni.children || ni.childNodes;
4928                 for(var j = 0, cj; cj = cn[j]; j++){
4929                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
4930                         result[++ri] = cj;
4931                     }
4932                 }
4933             }
4934         }else if(mode == "+"){
4935             var utag = tagName.toUpperCase();
4936             for(var i = 0, n; n = ns[i]; i++){
4937                 while((n = n.nextSibling) && n.nodeType != 1);
4938                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
4939                     result[++ri] = n;
4940                 }
4941             }
4942         }else if(mode == "~"){
4943             for(var i = 0, n; n = ns[i]; i++){
4944                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
4945                 if(n){
4946                     result[++ri] = n;
4947                 }
4948             }
4949         }
4950         return result;
4951     };
4952
4953     function concat(a, b){
4954         if(b.slice){
4955             return a.concat(b);
4956         }
4957         for(var i = 0, l = b.length; i < l; i++){
4958             a[a.length] = b[i];
4959         }
4960         return a;
4961     }
4962
4963     function byTag(cs, tagName){
4964         if(cs.tagName || cs == document){
4965             cs = [cs];
4966         }
4967         if(!tagName){
4968             return cs;
4969         }
4970         var r = [], ri = -1;
4971         tagName = tagName.toLowerCase();
4972         for(var i = 0, ci; ci = cs[i]; i++){
4973             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
4974                 r[++ri] = ci;
4975             }
4976         }
4977         return r;
4978     };
4979
4980     function byId(cs, attr, id){
4981         if(cs.tagName || cs == document){
4982             cs = [cs];
4983         }
4984         if(!id){
4985             return cs;
4986         }
4987         var r = [], ri = -1;
4988         for(var i = 0,ci; ci = cs[i]; i++){
4989             if(ci && ci.id == id){
4990                 r[++ri] = ci;
4991                 return r;
4992             }
4993         }
4994         return r;
4995     };
4996
4997     function byAttribute(cs, attr, value, op, custom){
4998         var r = [], ri = -1, st = custom=="{";
4999         var f = Roo.DomQuery.operators[op];
5000         for(var i = 0, ci; ci = cs[i]; i++){
5001             var a;
5002             if(st){
5003                 a = Roo.DomQuery.getStyle(ci, attr);
5004             }
5005             else if(attr == "class" || attr == "className"){
5006                 a = ci.className;
5007             }else if(attr == "for"){
5008                 a = ci.htmlFor;
5009             }else if(attr == "href"){
5010                 a = ci.getAttribute("href", 2);
5011             }else{
5012                 a = ci.getAttribute(attr);
5013             }
5014             if((f && f(a, value)) || (!f && a)){
5015                 r[++ri] = ci;
5016             }
5017         }
5018         return r;
5019     };
5020
5021     function byPseudo(cs, name, value){
5022         return Roo.DomQuery.pseudos[name](cs, value);
5023     };
5024
5025     // This is for IE MSXML which does not support expandos.
5026     // IE runs the same speed using setAttribute, however FF slows way down
5027     // and Safari completely fails so they need to continue to use expandos.
5028     var isIE = window.ActiveXObject ? true : false;
5029
5030     // this eval is stop the compressor from
5031     // renaming the variable to something shorter
5032     
5033     /** eval:var:batch */
5034     var batch = 30803; 
5035
5036     var key = 30803;
5037
5038     function nodupIEXml(cs){
5039         var d = ++key;
5040         cs[0].setAttribute("_nodup", d);
5041         var r = [cs[0]];
5042         for(var i = 1, len = cs.length; i < len; i++){
5043             var c = cs[i];
5044             if(!c.getAttribute("_nodup") != d){
5045                 c.setAttribute("_nodup", d);
5046                 r[r.length] = c;
5047             }
5048         }
5049         for(var i = 0, len = cs.length; i < len; i++){
5050             cs[i].removeAttribute("_nodup");
5051         }
5052         return r;
5053     }
5054
5055     function nodup(cs){
5056         if(!cs){
5057             return [];
5058         }
5059         var len = cs.length, c, i, r = cs, cj, ri = -1;
5060         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5061             return cs;
5062         }
5063         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5064             return nodupIEXml(cs);
5065         }
5066         var d = ++key;
5067         cs[0]._nodup = d;
5068         for(i = 1; c = cs[i]; i++){
5069             if(c._nodup != d){
5070                 c._nodup = d;
5071             }else{
5072                 r = [];
5073                 for(var j = 0; j < i; j++){
5074                     r[++ri] = cs[j];
5075                 }
5076                 for(j = i+1; cj = cs[j]; j++){
5077                     if(cj._nodup != d){
5078                         cj._nodup = d;
5079                         r[++ri] = cj;
5080                     }
5081                 }
5082                 return r;
5083             }
5084         }
5085         return r;
5086     }
5087
5088     function quickDiffIEXml(c1, c2){
5089         var d = ++key;
5090         for(var i = 0, len = c1.length; i < len; i++){
5091             c1[i].setAttribute("_qdiff", d);
5092         }
5093         var r = [];
5094         for(var i = 0, len = c2.length; i < len; i++){
5095             if(c2[i].getAttribute("_qdiff") != d){
5096                 r[r.length] = c2[i];
5097             }
5098         }
5099         for(var i = 0, len = c1.length; i < len; i++){
5100            c1[i].removeAttribute("_qdiff");
5101         }
5102         return r;
5103     }
5104
5105     function quickDiff(c1, c2){
5106         var len1 = c1.length;
5107         if(!len1){
5108             return c2;
5109         }
5110         if(isIE && c1[0].selectSingleNode){
5111             return quickDiffIEXml(c1, c2);
5112         }
5113         var d = ++key;
5114         for(var i = 0; i < len1; i++){
5115             c1[i]._qdiff = d;
5116         }
5117         var r = [];
5118         for(var i = 0, len = c2.length; i < len; i++){
5119             if(c2[i]._qdiff != d){
5120                 r[r.length] = c2[i];
5121             }
5122         }
5123         return r;
5124     }
5125
5126     function quickId(ns, mode, root, id){
5127         if(ns == root){
5128            var d = root.ownerDocument || root;
5129            return d.getElementById(id);
5130         }
5131         ns = getNodes(ns, mode, "*");
5132         return byId(ns, null, id);
5133     }
5134
5135     return {
5136         getStyle : function(el, name){
5137             return Roo.fly(el).getStyle(name);
5138         },
5139         /**
5140          * Compiles a selector/xpath query into a reusable function. The returned function
5141          * takes one parameter "root" (optional), which is the context node from where the query should start.
5142          * @param {String} selector The selector/xpath query
5143          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5144          * @return {Function}
5145          */
5146         compile : function(path, type){
5147             type = type || "select";
5148             
5149             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5150             var q = path, mode, lq;
5151             var tk = Roo.DomQuery.matchers;
5152             var tklen = tk.length;
5153             var mm;
5154
5155             // accept leading mode switch
5156             var lmode = q.match(modeRe);
5157             if(lmode && lmode[1]){
5158                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5159                 q = q.replace(lmode[1], "");
5160             }
5161             // strip leading slashes
5162             while(path.substr(0, 1)=="/"){
5163                 path = path.substr(1);
5164             }
5165
5166             while(q && lq != q){
5167                 lq = q;
5168                 var tm = q.match(tagTokenRe);
5169                 if(type == "select"){
5170                     if(tm){
5171                         if(tm[1] == "#"){
5172                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5173                         }else{
5174                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5175                         }
5176                         q = q.replace(tm[0], "");
5177                     }else if(q.substr(0, 1) != '@'){
5178                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5179                     }
5180                 }else{
5181                     if(tm){
5182                         if(tm[1] == "#"){
5183                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5184                         }else{
5185                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5186                         }
5187                         q = q.replace(tm[0], "");
5188                     }
5189                 }
5190                 while(!(mm = q.match(modeRe))){
5191                     var matched = false;
5192                     for(var j = 0; j < tklen; j++){
5193                         var t = tk[j];
5194                         var m = q.match(t.re);
5195                         if(m){
5196                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5197                                                     return m[i];
5198                                                 });
5199                             q = q.replace(m[0], "");
5200                             matched = true;
5201                             break;
5202                         }
5203                     }
5204                     // prevent infinite loop on bad selector
5205                     if(!matched){
5206                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5207                     }
5208                 }
5209                 if(mm[1]){
5210                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5211                     q = q.replace(mm[1], "");
5212                 }
5213             }
5214             fn[fn.length] = "return nodup(n);\n}";
5215             
5216              /** 
5217               * list of variables that need from compression as they are used by eval.
5218              *  eval:var:batch 
5219              *  eval:var:nodup
5220              *  eval:var:byTag
5221              *  eval:var:ById
5222              *  eval:var:getNodes
5223              *  eval:var:quickId
5224              *  eval:var:mode
5225              *  eval:var:root
5226              *  eval:var:n
5227              *  eval:var:byClassName
5228              *  eval:var:byPseudo
5229              *  eval:var:byAttribute
5230              *  eval:var:attrValue
5231              * 
5232              **/ 
5233             eval(fn.join(""));
5234             return f;
5235         },
5236
5237         /**
5238          * Selects a group of elements.
5239          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5240          * @param {Node} root (optional) The start of the query (defaults to document).
5241          * @return {Array}
5242          */
5243         select : function(path, root, type){
5244             if(!root || root == document){
5245                 root = document;
5246             }
5247             if(typeof root == "string"){
5248                 root = document.getElementById(root);
5249             }
5250             var paths = path.split(",");
5251             var results = [];
5252             for(var i = 0, len = paths.length; i < len; i++){
5253                 var p = paths[i].replace(trimRe, "");
5254                 if(!cache[p]){
5255                     cache[p] = Roo.DomQuery.compile(p);
5256                     if(!cache[p]){
5257                         throw p + " is not a valid selector";
5258                     }
5259                 }
5260                 var result = cache[p](root);
5261                 if(result && result != document){
5262                     results = results.concat(result);
5263                 }
5264             }
5265             if(paths.length > 1){
5266                 return nodup(results);
5267             }
5268             return results;
5269         },
5270
5271         /**
5272          * Selects a single element.
5273          * @param {String} selector The selector/xpath query
5274          * @param {Node} root (optional) The start of the query (defaults to document).
5275          * @return {Element}
5276          */
5277         selectNode : function(path, root){
5278             return Roo.DomQuery.select(path, root)[0];
5279         },
5280
5281         /**
5282          * Selects the value of a node, optionally replacing null with the defaultValue.
5283          * @param {String} selector The selector/xpath query
5284          * @param {Node} root (optional) The start of the query (defaults to document).
5285          * @param {String} defaultValue
5286          */
5287         selectValue : function(path, root, defaultValue){
5288             path = path.replace(trimRe, "");
5289             if(!valueCache[path]){
5290                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5291             }
5292             var n = valueCache[path](root);
5293             n = n[0] ? n[0] : n;
5294             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5295             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5296         },
5297
5298         /**
5299          * Selects the value of a node, parsing integers and floats.
5300          * @param {String} selector The selector/xpath query
5301          * @param {Node} root (optional) The start of the query (defaults to document).
5302          * @param {Number} defaultValue
5303          * @return {Number}
5304          */
5305         selectNumber : function(path, root, defaultValue){
5306             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5307             return parseFloat(v);
5308         },
5309
5310         /**
5311          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5312          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5313          * @param {String} selector The simple selector to test
5314          * @return {Boolean}
5315          */
5316         is : function(el, ss){
5317             if(typeof el == "string"){
5318                 el = document.getElementById(el);
5319             }
5320             var isArray = (el instanceof Array);
5321             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5322             return isArray ? (result.length == el.length) : (result.length > 0);
5323         },
5324
5325         /**
5326          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5327          * @param {Array} el An array of elements to filter
5328          * @param {String} selector The simple selector to test
5329          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5330          * the selector instead of the ones that match
5331          * @return {Array}
5332          */
5333         filter : function(els, ss, nonMatches){
5334             ss = ss.replace(trimRe, "");
5335             if(!simpleCache[ss]){
5336                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5337             }
5338             var result = simpleCache[ss](els);
5339             return nonMatches ? quickDiff(result, els) : result;
5340         },
5341
5342         /**
5343          * Collection of matching regular expressions and code snippets.
5344          */
5345         matchers : [{
5346                 re: /^\.([\w-]+)/,
5347                 select: 'n = byClassName(n, null, " {1} ");'
5348             }, {
5349                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5350                 select: 'n = byPseudo(n, "{1}", "{2}");'
5351             },{
5352                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5353                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5354             }, {
5355                 re: /^#([\w-]+)/,
5356                 select: 'n = byId(n, null, "{1}");'
5357             },{
5358                 re: /^@([\w-]+)/,
5359                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5360             }
5361         ],
5362
5363         /**
5364          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5365          * 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;.
5366          */
5367         operators : {
5368             "=" : function(a, v){
5369                 return a == v;
5370             },
5371             "!=" : function(a, v){
5372                 return a != v;
5373             },
5374             "^=" : function(a, v){
5375                 return a && a.substr(0, v.length) == v;
5376             },
5377             "$=" : function(a, v){
5378                 return a && a.substr(a.length-v.length) == v;
5379             },
5380             "*=" : function(a, v){
5381                 return a && a.indexOf(v) !== -1;
5382             },
5383             "%=" : function(a, v){
5384                 return (a % v) == 0;
5385             },
5386             "|=" : function(a, v){
5387                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5388             },
5389             "~=" : function(a, v){
5390                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5391             }
5392         },
5393
5394         /**
5395          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5396          * and the argument (if any) supplied in the selector.
5397          */
5398         pseudos : {
5399             "first-child" : function(c){
5400                 var r = [], ri = -1, n;
5401                 for(var i = 0, ci; ci = n = c[i]; i++){
5402                     while((n = n.previousSibling) && n.nodeType != 1);
5403                     if(!n){
5404                         r[++ri] = ci;
5405                     }
5406                 }
5407                 return r;
5408             },
5409
5410             "last-child" : function(c){
5411                 var r = [], ri = -1, n;
5412                 for(var i = 0, ci; ci = n = c[i]; i++){
5413                     while((n = n.nextSibling) && n.nodeType != 1);
5414                     if(!n){
5415                         r[++ri] = ci;
5416                     }
5417                 }
5418                 return r;
5419             },
5420
5421             "nth-child" : function(c, a) {
5422                 var r = [], ri = -1;
5423                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5424                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5425                 for(var i = 0, n; n = c[i]; i++){
5426                     var pn = n.parentNode;
5427                     if (batch != pn._batch) {
5428                         var j = 0;
5429                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5430                             if(cn.nodeType == 1){
5431                                cn.nodeIndex = ++j;
5432                             }
5433                         }
5434                         pn._batch = batch;
5435                     }
5436                     if (f == 1) {
5437                         if (l == 0 || n.nodeIndex == l){
5438                             r[++ri] = n;
5439                         }
5440                     } else if ((n.nodeIndex + l) % f == 0){
5441                         r[++ri] = n;
5442                     }
5443                 }
5444
5445                 return r;
5446             },
5447
5448             "only-child" : function(c){
5449                 var r = [], ri = -1;;
5450                 for(var i = 0, ci; ci = c[i]; i++){
5451                     if(!prev(ci) && !next(ci)){
5452                         r[++ri] = ci;
5453                     }
5454                 }
5455                 return r;
5456             },
5457
5458             "empty" : function(c){
5459                 var r = [], ri = -1;
5460                 for(var i = 0, ci; ci = c[i]; i++){
5461                     var cns = ci.childNodes, j = 0, cn, empty = true;
5462                     while(cn = cns[j]){
5463                         ++j;
5464                         if(cn.nodeType == 1 || cn.nodeType == 3){
5465                             empty = false;
5466                             break;
5467                         }
5468                     }
5469                     if(empty){
5470                         r[++ri] = ci;
5471                     }
5472                 }
5473                 return r;
5474             },
5475
5476             "contains" : function(c, v){
5477                 var r = [], ri = -1;
5478                 for(var i = 0, ci; ci = c[i]; i++){
5479                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5480                         r[++ri] = ci;
5481                     }
5482                 }
5483                 return r;
5484             },
5485
5486             "nodeValue" : function(c, v){
5487                 var r = [], ri = -1;
5488                 for(var i = 0, ci; ci = c[i]; i++){
5489                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5490                         r[++ri] = ci;
5491                     }
5492                 }
5493                 return r;
5494             },
5495
5496             "checked" : function(c){
5497                 var r = [], ri = -1;
5498                 for(var i = 0, ci; ci = c[i]; i++){
5499                     if(ci.checked == true){
5500                         r[++ri] = ci;
5501                     }
5502                 }
5503                 return r;
5504             },
5505
5506             "not" : function(c, ss){
5507                 return Roo.DomQuery.filter(c, ss, true);
5508             },
5509
5510             "odd" : function(c){
5511                 return this["nth-child"](c, "odd");
5512             },
5513
5514             "even" : function(c){
5515                 return this["nth-child"](c, "even");
5516             },
5517
5518             "nth" : function(c, a){
5519                 return c[a-1] || [];
5520             },
5521
5522             "first" : function(c){
5523                 return c[0] || [];
5524             },
5525
5526             "last" : function(c){
5527                 return c[c.length-1] || [];
5528             },
5529
5530             "has" : function(c, ss){
5531                 var s = Roo.DomQuery.select;
5532                 var r = [], ri = -1;
5533                 for(var i = 0, ci; ci = c[i]; i++){
5534                     if(s(ss, ci).length > 0){
5535                         r[++ri] = ci;
5536                     }
5537                 }
5538                 return r;
5539             },
5540
5541             "next" : function(c, ss){
5542                 var is = Roo.DomQuery.is;
5543                 var r = [], ri = -1;
5544                 for(var i = 0, ci; ci = c[i]; i++){
5545                     var n = next(ci);
5546                     if(n && is(n, ss)){
5547                         r[++ri] = ci;
5548                     }
5549                 }
5550                 return r;
5551             },
5552
5553             "prev" : function(c, ss){
5554                 var is = Roo.DomQuery.is;
5555                 var r = [], ri = -1;
5556                 for(var i = 0, ci; ci = c[i]; i++){
5557                     var n = prev(ci);
5558                     if(n && is(n, ss)){
5559                         r[++ri] = ci;
5560                     }
5561                 }
5562                 return r;
5563             }
5564         }
5565     };
5566 }();
5567
5568 /**
5569  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5570  * @param {String} path The selector/xpath query
5571  * @param {Node} root (optional) The start of the query (defaults to document).
5572  * @return {Array}
5573  * @member Roo
5574  * @method query
5575  */
5576 Roo.query = Roo.DomQuery.select;
5577 /*
5578  * Based on:
5579  * Ext JS Library 1.1.1
5580  * Copyright(c) 2006-2007, Ext JS, LLC.
5581  *
5582  * Originally Released Under LGPL - original licence link has changed is not relivant.
5583  *
5584  * Fork - LGPL
5585  * <script type="text/javascript">
5586  */
5587
5588 /**
5589  * @class Roo.util.Observable
5590  * Base class that provides a common interface for publishing events. Subclasses are expected to
5591  * to have a property "events" with all the events defined.<br>
5592  * For example:
5593  * <pre><code>
5594  Employee = function(name){
5595     this.name = name;
5596     this.addEvents({
5597         "fired" : true,
5598         "quit" : true
5599     });
5600  }
5601  Roo.extend(Employee, Roo.util.Observable);
5602 </code></pre>
5603  * @param {Object} config properties to use (incuding events / listeners)
5604  */
5605
5606 Roo.util.Observable = function(cfg){
5607     
5608     cfg = cfg|| {};
5609     this.addEvents(cfg.events || {});
5610     if (cfg.events) {
5611         delete cfg.events; // make sure
5612     }
5613      
5614     Roo.apply(this, cfg);
5615     
5616     if(this.listeners){
5617         this.on(this.listeners);
5618         delete this.listeners;
5619     }
5620 };
5621 Roo.util.Observable.prototype = {
5622     /** 
5623  * @cfg {Object} listeners  list of events and functions to call for this object, 
5624  * For example :
5625  * <pre><code>
5626     listeners :  { 
5627        'click' : function(e) {
5628            ..... 
5629         } ,
5630         .... 
5631     } 
5632   </code></pre>
5633  */
5634     
5635     
5636     /**
5637      * Fires the specified event with the passed parameters (minus the event name).
5638      * @param {String} eventName
5639      * @param {Object...} args Variable number of parameters are passed to handlers
5640      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5641      */
5642     fireEvent : function(){
5643         var ce = this.events[arguments[0].toLowerCase()];
5644         if(typeof ce == "object"){
5645             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5646         }else{
5647             return true;
5648         }
5649     },
5650
5651     // private
5652     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5653
5654     /**
5655      * Appends an event handler to this component
5656      * @param {String}   eventName The type of event to listen for
5657      * @param {Function} handler The method the event invokes
5658      * @param {Object}   scope (optional) The scope in which to execute the handler
5659      * function. The handler function's "this" context.
5660      * @param {Object}   options (optional) An object containing handler configuration
5661      * properties. This may contain any of the following properties:<ul>
5662      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5663      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5664      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5665      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5666      * by the specified number of milliseconds. If the event fires again within that time, the original
5667      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5668      * </ul><br>
5669      * <p>
5670      * <b>Combining Options</b><br>
5671      * Using the options argument, it is possible to combine different types of listeners:<br>
5672      * <br>
5673      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5674                 <pre><code>
5675                 el.on('click', this.onClick, this, {
5676                         single: true,
5677                 delay: 100,
5678                 forumId: 4
5679                 });
5680                 </code></pre>
5681      * <p>
5682      * <b>Attaching multiple handlers in 1 call</b><br>
5683      * The method also allows for a single argument to be passed which is a config object containing properties
5684      * which specify multiple handlers.
5685      * <pre><code>
5686                 el.on({
5687                         'click': {
5688                         fn: this.onClick,
5689                         scope: this,
5690                         delay: 100
5691                 }, 
5692                 'mouseover': {
5693                         fn: this.onMouseOver,
5694                         scope: this
5695                 },
5696                 'mouseout': {
5697                         fn: this.onMouseOut,
5698                         scope: this
5699                 }
5700                 });
5701                 </code></pre>
5702      * <p>
5703      * Or a shorthand syntax which passes the same scope object to all handlers:
5704         <pre><code>
5705                 el.on({
5706                         'click': this.onClick,
5707                 'mouseover': this.onMouseOver,
5708                 'mouseout': this.onMouseOut,
5709                 scope: this
5710                 });
5711                 </code></pre>
5712      */
5713     addListener : function(eventName, fn, scope, o){
5714         if(typeof eventName == "object"){
5715             o = eventName;
5716             for(var e in o){
5717                 if(this.filterOptRe.test(e)){
5718                     continue;
5719                 }
5720                 if(typeof o[e] == "function"){
5721                     // shared options
5722                     this.addListener(e, o[e], o.scope,  o);
5723                 }else{
5724                     // individual options
5725                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5726                 }
5727             }
5728             return;
5729         }
5730         o = (!o || typeof o == "boolean") ? {} : o;
5731         eventName = eventName.toLowerCase();
5732         var ce = this.events[eventName] || true;
5733         if(typeof ce == "boolean"){
5734             ce = new Roo.util.Event(this, eventName);
5735             this.events[eventName] = ce;
5736         }
5737         ce.addListener(fn, scope, o);
5738     },
5739
5740     /**
5741      * Removes a listener
5742      * @param {String}   eventName     The type of event to listen for
5743      * @param {Function} handler        The handler to remove
5744      * @param {Object}   scope  (optional) The scope (this object) for the handler
5745      */
5746     removeListener : function(eventName, fn, scope){
5747         var ce = this.events[eventName.toLowerCase()];
5748         if(typeof ce == "object"){
5749             ce.removeListener(fn, scope);
5750         }
5751     },
5752
5753     /**
5754      * Removes all listeners for this object
5755      */
5756     purgeListeners : function(){
5757         for(var evt in this.events){
5758             if(typeof this.events[evt] == "object"){
5759                  this.events[evt].clearListeners();
5760             }
5761         }
5762     },
5763
5764     relayEvents : function(o, events){
5765         var createHandler = function(ename){
5766             return function(){
5767                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5768             };
5769         };
5770         for(var i = 0, len = events.length; i < len; i++){
5771             var ename = events[i];
5772             if(!this.events[ename]){ this.events[ename] = true; };
5773             o.on(ename, createHandler(ename), this);
5774         }
5775     },
5776
5777     /**
5778      * Used to define events on this Observable
5779      * @param {Object} object The object with the events defined
5780      */
5781     addEvents : function(o){
5782         if(!this.events){
5783             this.events = {};
5784         }
5785         Roo.applyIf(this.events, o);
5786     },
5787
5788     /**
5789      * Checks to see if this object has any listeners for a specified event
5790      * @param {String} eventName The name of the event to check for
5791      * @return {Boolean} True if the event is being listened for, else false
5792      */
5793     hasListener : function(eventName){
5794         var e = this.events[eventName];
5795         return typeof e == "object" && e.listeners.length > 0;
5796     }
5797 };
5798 /**
5799  * Appends an event handler to this element (shorthand for addListener)
5800  * @param {String}   eventName     The type of event to listen for
5801  * @param {Function} handler        The method the event invokes
5802  * @param {Object}   scope (optional) The scope in which to execute the handler
5803  * function. The handler function's "this" context.
5804  * @param {Object}   options  (optional)
5805  * @method
5806  */
5807 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5808 /**
5809  * Removes a listener (shorthand for removeListener)
5810  * @param {String}   eventName     The type of event to listen for
5811  * @param {Function} handler        The handler to remove
5812  * @param {Object}   scope  (optional) The scope (this object) for the handler
5813  * @method
5814  */
5815 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5816
5817 /**
5818  * Starts capture on the specified Observable. All events will be passed
5819  * to the supplied function with the event name + standard signature of the event
5820  * <b>before</b> the event is fired. If the supplied function returns false,
5821  * the event will not fire.
5822  * @param {Observable} o The Observable to capture
5823  * @param {Function} fn The function to call
5824  * @param {Object} scope (optional) The scope (this object) for the fn
5825  * @static
5826  */
5827 Roo.util.Observable.capture = function(o, fn, scope){
5828     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5829 };
5830
5831 /**
5832  * Removes <b>all</b> added captures from the Observable.
5833  * @param {Observable} o The Observable to release
5834  * @static
5835  */
5836 Roo.util.Observable.releaseCapture = function(o){
5837     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5838 };
5839
5840 (function(){
5841
5842     var createBuffered = function(h, o, scope){
5843         var task = new Roo.util.DelayedTask();
5844         return function(){
5845             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5846         };
5847     };
5848
5849     var createSingle = function(h, e, fn, scope){
5850         return function(){
5851             e.removeListener(fn, scope);
5852             return h.apply(scope, arguments);
5853         };
5854     };
5855
5856     var createDelayed = function(h, o, scope){
5857         return function(){
5858             var args = Array.prototype.slice.call(arguments, 0);
5859             setTimeout(function(){
5860                 h.apply(scope, args);
5861             }, o.delay || 10);
5862         };
5863     };
5864
5865     Roo.util.Event = function(obj, name){
5866         this.name = name;
5867         this.obj = obj;
5868         this.listeners = [];
5869     };
5870
5871     Roo.util.Event.prototype = {
5872         addListener : function(fn, scope, options){
5873             var o = options || {};
5874             scope = scope || this.obj;
5875             if(!this.isListening(fn, scope)){
5876                 var l = {fn: fn, scope: scope, options: o};
5877                 var h = fn;
5878                 if(o.delay){
5879                     h = createDelayed(h, o, scope);
5880                 }
5881                 if(o.single){
5882                     h = createSingle(h, this, fn, scope);
5883                 }
5884                 if(o.buffer){
5885                     h = createBuffered(h, o, scope);
5886                 }
5887                 l.fireFn = h;
5888                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
5889                     this.listeners.push(l);
5890                 }else{
5891                     this.listeners = this.listeners.slice(0);
5892                     this.listeners.push(l);
5893                 }
5894             }
5895         },
5896
5897         findListener : function(fn, scope){
5898             scope = scope || this.obj;
5899             var ls = this.listeners;
5900             for(var i = 0, len = ls.length; i < len; i++){
5901                 var l = ls[i];
5902                 if(l.fn == fn && l.scope == scope){
5903                     return i;
5904                 }
5905             }
5906             return -1;
5907         },
5908
5909         isListening : function(fn, scope){
5910             return this.findListener(fn, scope) != -1;
5911         },
5912
5913         removeListener : function(fn, scope){
5914             var index;
5915             if((index = this.findListener(fn, scope)) != -1){
5916                 if(!this.firing){
5917                     this.listeners.splice(index, 1);
5918                 }else{
5919                     this.listeners = this.listeners.slice(0);
5920                     this.listeners.splice(index, 1);
5921                 }
5922                 return true;
5923             }
5924             return false;
5925         },
5926
5927         clearListeners : function(){
5928             this.listeners = [];
5929         },
5930
5931         fire : function(){
5932             var ls = this.listeners, scope, len = ls.length;
5933             if(len > 0){
5934                 this.firing = true;
5935                 var args = Array.prototype.slice.call(arguments, 0);
5936                 for(var i = 0; i < len; i++){
5937                     var l = ls[i];
5938                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
5939                         this.firing = false;
5940                         return false;
5941                     }
5942                 }
5943                 this.firing = false;
5944             }
5945             return true;
5946         }
5947     };
5948 })();/*
5949  * Based on:
5950  * Ext JS Library 1.1.1
5951  * Copyright(c) 2006-2007, Ext JS, LLC.
5952  *
5953  * Originally Released Under LGPL - original licence link has changed is not relivant.
5954  *
5955  * Fork - LGPL
5956  * <script type="text/javascript">
5957  */
5958
5959 /**
5960  * @class Roo.EventManager
5961  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
5962  * several useful events directly.
5963  * See {@link Roo.EventObject} for more details on normalized event objects.
5964  * @singleton
5965  */
5966 Roo.EventManager = function(){
5967     var docReadyEvent, docReadyProcId, docReadyState = false;
5968     var resizeEvent, resizeTask, textEvent, textSize;
5969     var E = Roo.lib.Event;
5970     var D = Roo.lib.Dom;
5971
5972
5973     var fireDocReady = function(){
5974         if(!docReadyState){
5975             docReadyState = true;
5976             Roo.isReady = true;
5977             if(docReadyProcId){
5978                 clearInterval(docReadyProcId);
5979             }
5980             if(Roo.isGecko || Roo.isOpera) {
5981                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
5982             }
5983             if(Roo.isIE){
5984                 var defer = document.getElementById("ie-deferred-loader");
5985                 if(defer){
5986                     defer.onreadystatechange = null;
5987                     defer.parentNode.removeChild(defer);
5988                 }
5989             }
5990             if(docReadyEvent){
5991                 docReadyEvent.fire();
5992                 docReadyEvent.clearListeners();
5993             }
5994         }
5995     };
5996     
5997     var initDocReady = function(){
5998         docReadyEvent = new Roo.util.Event();
5999         if(Roo.isGecko || Roo.isOpera) {
6000             document.addEventListener("DOMContentLoaded", fireDocReady, false);
6001         }else if(Roo.isIE){
6002             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
6003             var defer = document.getElementById("ie-deferred-loader");
6004             defer.onreadystatechange = function(){
6005                 if(this.readyState == "complete"){
6006                     fireDocReady();
6007                 }
6008             };
6009         }else if(Roo.isSafari){ 
6010             docReadyProcId = setInterval(function(){
6011                 var rs = document.readyState;
6012                 if(rs == "complete") {
6013                     fireDocReady();     
6014                  }
6015             }, 10);
6016         }
6017         // no matter what, make sure it fires on load
6018         E.on(window, "load", fireDocReady);
6019     };
6020
6021     var createBuffered = function(h, o){
6022         var task = new Roo.util.DelayedTask(h);
6023         return function(e){
6024             // create new event object impl so new events don't wipe out properties
6025             e = new Roo.EventObjectImpl(e);
6026             task.delay(o.buffer, h, null, [e]);
6027         };
6028     };
6029
6030     var createSingle = function(h, el, ename, fn){
6031         return function(e){
6032             Roo.EventManager.removeListener(el, ename, fn);
6033             h(e);
6034         };
6035     };
6036
6037     var createDelayed = function(h, o){
6038         return function(e){
6039             // create new event object impl so new events don't wipe out properties
6040             e = new Roo.EventObjectImpl(e);
6041             setTimeout(function(){
6042                 h(e);
6043             }, o.delay || 10);
6044         };
6045     };
6046
6047     var listen = function(element, ename, opt, fn, scope){
6048         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6049         fn = fn || o.fn; scope = scope || o.scope;
6050         var el = Roo.getDom(element);
6051         if(!el){
6052             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6053         }
6054         var h = function(e){
6055             e = Roo.EventObject.setEvent(e);
6056             var t;
6057             if(o.delegate){
6058                 t = e.getTarget(o.delegate, el);
6059                 if(!t){
6060                     return;
6061                 }
6062             }else{
6063                 t = e.target;
6064             }
6065             if(o.stopEvent === true){
6066                 e.stopEvent();
6067             }
6068             if(o.preventDefault === true){
6069                e.preventDefault();
6070             }
6071             if(o.stopPropagation === true){
6072                 e.stopPropagation();
6073             }
6074
6075             if(o.normalized === false){
6076                 e = e.browserEvent;
6077             }
6078
6079             fn.call(scope || el, e, t, o);
6080         };
6081         if(o.delay){
6082             h = createDelayed(h, o);
6083         }
6084         if(o.single){
6085             h = createSingle(h, el, ename, fn);
6086         }
6087         if(o.buffer){
6088             h = createBuffered(h, o);
6089         }
6090         fn._handlers = fn._handlers || [];
6091         fn._handlers.push([Roo.id(el), ename, h]);
6092
6093         E.on(el, ename, h);
6094         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6095             el.addEventListener("DOMMouseScroll", h, false);
6096             E.on(window, 'unload', function(){
6097                 el.removeEventListener("DOMMouseScroll", h, false);
6098             });
6099         }
6100         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6101             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6102         }
6103         return h;
6104     };
6105
6106     var stopListening = function(el, ename, fn){
6107         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6108         if(hds){
6109             for(var i = 0, len = hds.length; i < len; i++){
6110                 var h = hds[i];
6111                 if(h[0] == id && h[1] == ename){
6112                     hd = h[2];
6113                     hds.splice(i, 1);
6114                     break;
6115                 }
6116             }
6117         }
6118         E.un(el, ename, hd);
6119         el = Roo.getDom(el);
6120         if(ename == "mousewheel" && el.addEventListener){
6121             el.removeEventListener("DOMMouseScroll", hd, false);
6122         }
6123         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6124             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6125         }
6126     };
6127
6128     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6129     
6130     var pub = {
6131         
6132         
6133         /** 
6134          * Fix for doc tools
6135          * @scope Roo.EventManager
6136          */
6137         
6138         
6139         /** 
6140          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6141          * object with a Roo.EventObject
6142          * @param {Function} fn        The method the event invokes
6143          * @param {Object}   scope    An object that becomes the scope of the handler
6144          * @param {boolean}  override If true, the obj passed in becomes
6145          *                             the execution scope of the listener
6146          * @return {Function} The wrapped function
6147          * @deprecated
6148          */
6149         wrap : function(fn, scope, override){
6150             return function(e){
6151                 Roo.EventObject.setEvent(e);
6152                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6153             };
6154         },
6155         
6156         /**
6157      * Appends an event handler to an element (shorthand for addListener)
6158      * @param {String/HTMLElement}   element        The html element or id to assign the
6159      * @param {String}   eventName The type of event to listen for
6160      * @param {Function} handler The method the event invokes
6161      * @param {Object}   scope (optional) The scope in which to execute the handler
6162      * function. The handler function's "this" context.
6163      * @param {Object}   options (optional) An object containing handler configuration
6164      * properties. This may contain any of the following properties:<ul>
6165      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6166      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6167      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6168      * <li>preventDefault {Boolean} True to prevent the default action</li>
6169      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6170      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6171      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6172      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6173      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6174      * by the specified number of milliseconds. If the event fires again within that time, the original
6175      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6176      * </ul><br>
6177      * <p>
6178      * <b>Combining Options</b><br>
6179      * Using the options argument, it is possible to combine different types of listeners:<br>
6180      * <br>
6181      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6182      * Code:<pre><code>
6183 el.on('click', this.onClick, this, {
6184     single: true,
6185     delay: 100,
6186     stopEvent : true,
6187     forumId: 4
6188 });</code></pre>
6189      * <p>
6190      * <b>Attaching multiple handlers in 1 call</b><br>
6191       * The method also allows for a single argument to be passed which is a config object containing properties
6192      * which specify multiple handlers.
6193      * <p>
6194      * Code:<pre><code>
6195 el.on({
6196     'click' : {
6197         fn: this.onClick
6198         scope: this,
6199         delay: 100
6200     },
6201     'mouseover' : {
6202         fn: this.onMouseOver
6203         scope: this
6204     },
6205     'mouseout' : {
6206         fn: this.onMouseOut
6207         scope: this
6208     }
6209 });</code></pre>
6210      * <p>
6211      * Or a shorthand syntax:<br>
6212      * Code:<pre><code>
6213 el.on({
6214     'click' : this.onClick,
6215     'mouseover' : this.onMouseOver,
6216     'mouseout' : this.onMouseOut
6217     scope: this
6218 });</code></pre>
6219      */
6220         addListener : function(element, eventName, fn, scope, options){
6221             if(typeof eventName == "object"){
6222                 var o = eventName;
6223                 for(var e in o){
6224                     if(propRe.test(e)){
6225                         continue;
6226                     }
6227                     if(typeof o[e] == "function"){
6228                         // shared options
6229                         listen(element, e, o, o[e], o.scope);
6230                     }else{
6231                         // individual options
6232                         listen(element, e, o[e]);
6233                     }
6234                 }
6235                 return;
6236             }
6237             return listen(element, eventName, options, fn, scope);
6238         },
6239         
6240         /**
6241          * Removes an event handler
6242          *
6243          * @param {String/HTMLElement}   element        The id or html element to remove the 
6244          *                             event from
6245          * @param {String}   eventName     The type of event
6246          * @param {Function} fn
6247          * @return {Boolean} True if a listener was actually removed
6248          */
6249         removeListener : function(element, eventName, fn){
6250             return stopListening(element, eventName, fn);
6251         },
6252         
6253         /**
6254          * Fires when the document is ready (before onload and before images are loaded). Can be 
6255          * accessed shorthanded Roo.onReady().
6256          * @param {Function} fn        The method the event invokes
6257          * @param {Object}   scope    An  object that becomes the scope of the handler
6258          * @param {boolean}  options
6259          */
6260         onDocumentReady : function(fn, scope, options){
6261             if(docReadyState){ // if it already fired
6262                 docReadyEvent.addListener(fn, scope, options);
6263                 docReadyEvent.fire();
6264                 docReadyEvent.clearListeners();
6265                 return;
6266             }
6267             if(!docReadyEvent){
6268                 initDocReady();
6269             }
6270             docReadyEvent.addListener(fn, scope, options);
6271         },
6272         
6273         /**
6274          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6275          * @param {Function} fn        The method the event invokes
6276          * @param {Object}   scope    An object that becomes the scope of the handler
6277          * @param {boolean}  options
6278          */
6279         onWindowResize : function(fn, scope, options){
6280             if(!resizeEvent){
6281                 resizeEvent = new Roo.util.Event();
6282                 resizeTask = new Roo.util.DelayedTask(function(){
6283                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6284                 });
6285                 E.on(window, "resize", function(){
6286                     if(Roo.isIE){
6287                         resizeTask.delay(50);
6288                     }else{
6289                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6290                     }
6291                 });
6292             }
6293             resizeEvent.addListener(fn, scope, options);
6294         },
6295
6296         /**
6297          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6298          * @param {Function} fn        The method the event invokes
6299          * @param {Object}   scope    An object that becomes the scope of the handler
6300          * @param {boolean}  options
6301          */
6302         onTextResize : function(fn, scope, options){
6303             if(!textEvent){
6304                 textEvent = new Roo.util.Event();
6305                 var textEl = new Roo.Element(document.createElement('div'));
6306                 textEl.dom.className = 'x-text-resize';
6307                 textEl.dom.innerHTML = 'X';
6308                 textEl.appendTo(document.body);
6309                 textSize = textEl.dom.offsetHeight;
6310                 setInterval(function(){
6311                     if(textEl.dom.offsetHeight != textSize){
6312                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6313                     }
6314                 }, this.textResizeInterval);
6315             }
6316             textEvent.addListener(fn, scope, options);
6317         },
6318
6319         /**
6320          * Removes the passed window resize listener.
6321          * @param {Function} fn        The method the event invokes
6322          * @param {Object}   scope    The scope of handler
6323          */
6324         removeResizeListener : function(fn, scope){
6325             if(resizeEvent){
6326                 resizeEvent.removeListener(fn, scope);
6327             }
6328         },
6329
6330         // private
6331         fireResize : function(){
6332             if(resizeEvent){
6333                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6334             }   
6335         },
6336         /**
6337          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6338          */
6339         ieDeferSrc : false,
6340         /**
6341          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6342          */
6343         textResizeInterval : 50
6344     };
6345     
6346     /**
6347      * Fix for doc tools
6348      * @scopeAlias pub=Roo.EventManager
6349      */
6350     
6351      /**
6352      * Appends an event handler to an element (shorthand for addListener)
6353      * @param {String/HTMLElement}   element        The html element or id to assign the
6354      * @param {String}   eventName The type of event to listen for
6355      * @param {Function} handler The method the event invokes
6356      * @param {Object}   scope (optional) The scope in which to execute the handler
6357      * function. The handler function's "this" context.
6358      * @param {Object}   options (optional) An object containing handler configuration
6359      * properties. This may contain any of the following properties:<ul>
6360      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6361      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6362      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6363      * <li>preventDefault {Boolean} True to prevent the default action</li>
6364      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6365      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6366      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6367      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6368      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6369      * by the specified number of milliseconds. If the event fires again within that time, the original
6370      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6371      * </ul><br>
6372      * <p>
6373      * <b>Combining Options</b><br>
6374      * Using the options argument, it is possible to combine different types of listeners:<br>
6375      * <br>
6376      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6377      * Code:<pre><code>
6378 el.on('click', this.onClick, this, {
6379     single: true,
6380     delay: 100,
6381     stopEvent : true,
6382     forumId: 4
6383 });</code></pre>
6384      * <p>
6385      * <b>Attaching multiple handlers in 1 call</b><br>
6386       * The method also allows for a single argument to be passed which is a config object containing properties
6387      * which specify multiple handlers.
6388      * <p>
6389      * Code:<pre><code>
6390 el.on({
6391     'click' : {
6392         fn: this.onClick
6393         scope: this,
6394         delay: 100
6395     },
6396     'mouseover' : {
6397         fn: this.onMouseOver
6398         scope: this
6399     },
6400     'mouseout' : {
6401         fn: this.onMouseOut
6402         scope: this
6403     }
6404 });</code></pre>
6405      * <p>
6406      * Or a shorthand syntax:<br>
6407      * Code:<pre><code>
6408 el.on({
6409     'click' : this.onClick,
6410     'mouseover' : this.onMouseOver,
6411     'mouseout' : this.onMouseOut
6412     scope: this
6413 });</code></pre>
6414      */
6415     pub.on = pub.addListener;
6416     pub.un = pub.removeListener;
6417
6418     pub.stoppedMouseDownEvent = new Roo.util.Event();
6419     return pub;
6420 }();
6421 /**
6422   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6423   * @param {Function} fn        The method the event invokes
6424   * @param {Object}   scope    An  object that becomes the scope of the handler
6425   * @param {boolean}  override If true, the obj passed in becomes
6426   *                             the execution scope of the listener
6427   * @member Roo
6428   * @method onReady
6429  */
6430 Roo.onReady = Roo.EventManager.onDocumentReady;
6431
6432 Roo.onReady(function(){
6433     var bd = Roo.get(document.body);
6434     if(!bd){ return; }
6435
6436     var cls = [
6437             Roo.isIE ? "roo-ie"
6438             : Roo.isGecko ? "roo-gecko"
6439             : Roo.isOpera ? "roo-opera"
6440             : Roo.isSafari ? "roo-safari" : ""];
6441
6442     if(Roo.isMac){
6443         cls.push("roo-mac");
6444     }
6445     if(Roo.isLinux){
6446         cls.push("roo-linux");
6447     }
6448     if(Roo.isBorderBox){
6449         cls.push('roo-border-box');
6450     }
6451     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6452         var p = bd.dom.parentNode;
6453         if(p){
6454             p.className += ' roo-strict';
6455         }
6456     }
6457     bd.addClass(cls.join(' '));
6458 });
6459
6460 /**
6461  * @class Roo.EventObject
6462  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6463  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6464  * Example:
6465  * <pre><code>
6466  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6467     e.preventDefault();
6468     var target = e.getTarget();
6469     ...
6470  }
6471  var myDiv = Roo.get("myDiv");
6472  myDiv.on("click", handleClick);
6473  //or
6474  Roo.EventManager.on("myDiv", 'click', handleClick);
6475  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6476  </code></pre>
6477  * @singleton
6478  */
6479 Roo.EventObject = function(){
6480     
6481     var E = Roo.lib.Event;
6482     
6483     // safari keypress events for special keys return bad keycodes
6484     var safariKeys = {
6485         63234 : 37, // left
6486         63235 : 39, // right
6487         63232 : 38, // up
6488         63233 : 40, // down
6489         63276 : 33, // page up
6490         63277 : 34, // page down
6491         63272 : 46, // delete
6492         63273 : 36, // home
6493         63275 : 35  // end
6494     };
6495
6496     // normalize button clicks
6497     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6498                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6499
6500     Roo.EventObjectImpl = function(e){
6501         if(e){
6502             this.setEvent(e.browserEvent || e);
6503         }
6504     };
6505     Roo.EventObjectImpl.prototype = {
6506         /**
6507          * Used to fix doc tools.
6508          * @scope Roo.EventObject.prototype
6509          */
6510             
6511
6512         
6513         
6514         /** The normal browser event */
6515         browserEvent : null,
6516         /** The button pressed in a mouse event */
6517         button : -1,
6518         /** True if the shift key was down during the event */
6519         shiftKey : false,
6520         /** True if the control key was down during the event */
6521         ctrlKey : false,
6522         /** True if the alt key was down during the event */
6523         altKey : false,
6524
6525         /** Key constant 
6526         * @type Number */
6527         BACKSPACE : 8,
6528         /** Key constant 
6529         * @type Number */
6530         TAB : 9,
6531         /** Key constant 
6532         * @type Number */
6533         RETURN : 13,
6534         /** Key constant 
6535         * @type Number */
6536         ENTER : 13,
6537         /** Key constant 
6538         * @type Number */
6539         SHIFT : 16,
6540         /** Key constant 
6541         * @type Number */
6542         CONTROL : 17,
6543         /** Key constant 
6544         * @type Number */
6545         ESC : 27,
6546         /** Key constant 
6547         * @type Number */
6548         SPACE : 32,
6549         /** Key constant 
6550         * @type Number */
6551         PAGEUP : 33,
6552         /** Key constant 
6553         * @type Number */
6554         PAGEDOWN : 34,
6555         /** Key constant 
6556         * @type Number */
6557         END : 35,
6558         /** Key constant 
6559         * @type Number */
6560         HOME : 36,
6561         /** Key constant 
6562         * @type Number */
6563         LEFT : 37,
6564         /** Key constant 
6565         * @type Number */
6566         UP : 38,
6567         /** Key constant 
6568         * @type Number */
6569         RIGHT : 39,
6570         /** Key constant 
6571         * @type Number */
6572         DOWN : 40,
6573         /** Key constant 
6574         * @type Number */
6575         DELETE : 46,
6576         /** Key constant 
6577         * @type Number */
6578         F5 : 116,
6579
6580            /** @private */
6581         setEvent : function(e){
6582             if(e == this || (e && e.browserEvent)){ // already wrapped
6583                 return e;
6584             }
6585             this.browserEvent = e;
6586             if(e){
6587                 // normalize buttons
6588                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6589                 if(e.type == 'click' && this.button == -1){
6590                     this.button = 0;
6591                 }
6592                 this.type = e.type;
6593                 this.shiftKey = e.shiftKey;
6594                 // mac metaKey behaves like ctrlKey
6595                 this.ctrlKey = e.ctrlKey || e.metaKey;
6596                 this.altKey = e.altKey;
6597                 // in getKey these will be normalized for the mac
6598                 this.keyCode = e.keyCode;
6599                 // keyup warnings on firefox.
6600                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6601                 // cache the target for the delayed and or buffered events
6602                 this.target = E.getTarget(e);
6603                 // same for XY
6604                 this.xy = E.getXY(e);
6605             }else{
6606                 this.button = -1;
6607                 this.shiftKey = false;
6608                 this.ctrlKey = false;
6609                 this.altKey = false;
6610                 this.keyCode = 0;
6611                 this.charCode =0;
6612                 this.target = null;
6613                 this.xy = [0, 0];
6614             }
6615             return this;
6616         },
6617
6618         /**
6619          * Stop the event (preventDefault and stopPropagation)
6620          */
6621         stopEvent : function(){
6622             if(this.browserEvent){
6623                 if(this.browserEvent.type == 'mousedown'){
6624                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6625                 }
6626                 E.stopEvent(this.browserEvent);
6627             }
6628         },
6629
6630         /**
6631          * Prevents the browsers default handling of the event.
6632          */
6633         preventDefault : function(){
6634             if(this.browserEvent){
6635                 E.preventDefault(this.browserEvent);
6636             }
6637         },
6638
6639         /** @private */
6640         isNavKeyPress : function(){
6641             var k = this.keyCode;
6642             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6643             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6644         },
6645
6646         isSpecialKey : function(){
6647             var k = this.keyCode;
6648             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6649             (k == 16) || (k == 17) ||
6650             (k >= 18 && k <= 20) ||
6651             (k >= 33 && k <= 35) ||
6652             (k >= 36 && k <= 39) ||
6653             (k >= 44 && k <= 45);
6654         },
6655         /**
6656          * Cancels bubbling of the event.
6657          */
6658         stopPropagation : function(){
6659             if(this.browserEvent){
6660                 if(this.type == 'mousedown'){
6661                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6662                 }
6663                 E.stopPropagation(this.browserEvent);
6664             }
6665         },
6666
6667         /**
6668          * Gets the key code for the event.
6669          * @return {Number}
6670          */
6671         getCharCode : function(){
6672             return this.charCode || this.keyCode;
6673         },
6674
6675         /**
6676          * Returns a normalized keyCode for the event.
6677          * @return {Number} The key code
6678          */
6679         getKey : function(){
6680             var k = this.keyCode || this.charCode;
6681             return Roo.isSafari ? (safariKeys[k] || k) : k;
6682         },
6683
6684         /**
6685          * Gets the x coordinate of the event.
6686          * @return {Number}
6687          */
6688         getPageX : function(){
6689             return this.xy[0];
6690         },
6691
6692         /**
6693          * Gets the y coordinate of the event.
6694          * @return {Number}
6695          */
6696         getPageY : function(){
6697             return this.xy[1];
6698         },
6699
6700         /**
6701          * Gets the time of the event.
6702          * @return {Number}
6703          */
6704         getTime : function(){
6705             if(this.browserEvent){
6706                 return E.getTime(this.browserEvent);
6707             }
6708             return null;
6709         },
6710
6711         /**
6712          * Gets the page coordinates of the event.
6713          * @return {Array} The xy values like [x, y]
6714          */
6715         getXY : function(){
6716             return this.xy;
6717         },
6718
6719         /**
6720          * Gets the target for the event.
6721          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6722          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6723                 search as a number or element (defaults to 10 || document.body)
6724          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6725          * @return {HTMLelement}
6726          */
6727         getTarget : function(selector, maxDepth, returnEl){
6728             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6729         },
6730         /**
6731          * Gets the related target.
6732          * @return {HTMLElement}
6733          */
6734         getRelatedTarget : function(){
6735             if(this.browserEvent){
6736                 return E.getRelatedTarget(this.browserEvent);
6737             }
6738             return null;
6739         },
6740
6741         /**
6742          * Normalizes mouse wheel delta across browsers
6743          * @return {Number} The delta
6744          */
6745         getWheelDelta : function(){
6746             var e = this.browserEvent;
6747             var delta = 0;
6748             if(e.wheelDelta){ /* IE/Opera. */
6749                 delta = e.wheelDelta/120;
6750             }else if(e.detail){ /* Mozilla case. */
6751                 delta = -e.detail/3;
6752             }
6753             return delta;
6754         },
6755
6756         /**
6757          * Returns true if the control, meta, shift or alt key was pressed during this event.
6758          * @return {Boolean}
6759          */
6760         hasModifier : function(){
6761             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6762         },
6763
6764         /**
6765          * Returns true if the target of this event equals el or is a child of el
6766          * @param {String/HTMLElement/Element} el
6767          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6768          * @return {Boolean}
6769          */
6770         within : function(el, related){
6771             var t = this[related ? "getRelatedTarget" : "getTarget"]();
6772             return t && Roo.fly(el).contains(t);
6773         },
6774
6775         getPoint : function(){
6776             return new Roo.lib.Point(this.xy[0], this.xy[1]);
6777         }
6778     };
6779
6780     return new Roo.EventObjectImpl();
6781 }();
6782             
6783     /*
6784  * Based on:
6785  * Ext JS Library 1.1.1
6786  * Copyright(c) 2006-2007, Ext JS, LLC.
6787  *
6788  * Originally Released Under LGPL - original licence link has changed is not relivant.
6789  *
6790  * Fork - LGPL
6791  * <script type="text/javascript">
6792  */
6793
6794  
6795 // was in Composite Element!??!?!
6796  
6797 (function(){
6798     var D = Roo.lib.Dom;
6799     var E = Roo.lib.Event;
6800     var A = Roo.lib.Anim;
6801
6802     // local style camelizing for speed
6803     var propCache = {};
6804     var camelRe = /(-[a-z])/gi;
6805     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
6806     var view = document.defaultView;
6807
6808 /**
6809  * @class Roo.Element
6810  * Represents an Element in the DOM.<br><br>
6811  * Usage:<br>
6812 <pre><code>
6813 var el = Roo.get("my-div");
6814
6815 // or with getEl
6816 var el = getEl("my-div");
6817
6818 // or with a DOM element
6819 var el = Roo.get(myDivElement);
6820 </code></pre>
6821  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
6822  * each call instead of constructing a new one.<br><br>
6823  * <b>Animations</b><br />
6824  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
6825  * should either be a boolean (true) or an object literal with animation options. The animation options are:
6826 <pre>
6827 Option    Default   Description
6828 --------- --------  ---------------------------------------------
6829 duration  .35       The duration of the animation in seconds
6830 easing    easeOut   The YUI easing method
6831 callback  none      A function to execute when the anim completes
6832 scope     this      The scope (this) of the callback function
6833 </pre>
6834 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
6835 * manipulate the animation. Here's an example:
6836 <pre><code>
6837 var el = Roo.get("my-div");
6838
6839 // no animation
6840 el.setWidth(100);
6841
6842 // default animation
6843 el.setWidth(100, true);
6844
6845 // animation with some options set
6846 el.setWidth(100, {
6847     duration: 1,
6848     callback: this.foo,
6849     scope: this
6850 });
6851
6852 // using the "anim" property to get the Anim object
6853 var opt = {
6854     duration: 1,
6855     callback: this.foo,
6856     scope: this
6857 };
6858 el.setWidth(100, opt);
6859 ...
6860 if(opt.anim.isAnimated()){
6861     opt.anim.stop();
6862 }
6863 </code></pre>
6864 * <b> Composite (Collections of) Elements</b><br />
6865  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
6866  * @constructor Create a new Element directly.
6867  * @param {String/HTMLElement} element
6868  * @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).
6869  */
6870     Roo.Element = function(element, forceNew){
6871         var dom = typeof element == "string" ?
6872                 document.getElementById(element) : element;
6873         if(!dom){ // invalid id/element
6874             return null;
6875         }
6876         var id = dom.id;
6877         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
6878             return Roo.Element.cache[id];
6879         }
6880
6881         /**
6882          * The DOM element
6883          * @type HTMLElement
6884          */
6885         this.dom = dom;
6886
6887         /**
6888          * The DOM element ID
6889          * @type String
6890          */
6891         this.id = id || Roo.id(dom);
6892     };
6893
6894     var El = Roo.Element;
6895
6896     El.prototype = {
6897         /**
6898          * The element's default display mode  (defaults to "")
6899          * @type String
6900          */
6901         originalDisplay : "",
6902
6903         visibilityMode : 1,
6904         /**
6905          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
6906          * @type String
6907          */
6908         defaultUnit : "px",
6909         /**
6910          * Sets the element's visibility mode. When setVisible() is called it
6911          * will use this to determine whether to set the visibility or the display property.
6912          * @param visMode Element.VISIBILITY or Element.DISPLAY
6913          * @return {Roo.Element} this
6914          */
6915         setVisibilityMode : function(visMode){
6916             this.visibilityMode = visMode;
6917             return this;
6918         },
6919         /**
6920          * Convenience method for setVisibilityMode(Element.DISPLAY)
6921          * @param {String} display (optional) What to set display to when visible
6922          * @return {Roo.Element} this
6923          */
6924         enableDisplayMode : function(display){
6925             this.setVisibilityMode(El.DISPLAY);
6926             if(typeof display != "undefined") this.originalDisplay = display;
6927             return this;
6928         },
6929
6930         /**
6931          * 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)
6932          * @param {String} selector The simple selector to test
6933          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6934                 search as a number or element (defaults to 10 || document.body)
6935          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6936          * @return {HTMLElement} The matching DOM node (or null if no match was found)
6937          */
6938         findParent : function(simpleSelector, maxDepth, returnEl){
6939             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
6940             maxDepth = maxDepth || 50;
6941             if(typeof maxDepth != "number"){
6942                 stopEl = Roo.getDom(maxDepth);
6943                 maxDepth = 10;
6944             }
6945             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
6946                 if(dq.is(p, simpleSelector)){
6947                     return returnEl ? Roo.get(p) : p;
6948                 }
6949                 depth++;
6950                 p = p.parentNode;
6951             }
6952             return null;
6953         },
6954
6955
6956         /**
6957          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
6958          * @param {String} selector The simple selector to test
6959          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6960                 search as a number or element (defaults to 10 || document.body)
6961          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6962          * @return {HTMLElement} The matching DOM node (or null if no match was found)
6963          */
6964         findParentNode : function(simpleSelector, maxDepth, returnEl){
6965             var p = Roo.fly(this.dom.parentNode, '_internal');
6966             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
6967         },
6968
6969         /**
6970          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
6971          * This is a shortcut for findParentNode() that always returns an Roo.Element.
6972          * @param {String} selector The simple selector to test
6973          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6974                 search as a number or element (defaults to 10 || document.body)
6975          * @return {Roo.Element} The matching DOM node (or null if no match was found)
6976          */
6977         up : function(simpleSelector, maxDepth){
6978             return this.findParentNode(simpleSelector, maxDepth, true);
6979         },
6980
6981
6982
6983         /**
6984          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
6985          * @param {String} selector The simple selector to test
6986          * @return {Boolean} True if this element matches the selector, else false
6987          */
6988         is : function(simpleSelector){
6989             return Roo.DomQuery.is(this.dom, simpleSelector);
6990         },
6991
6992         /**
6993          * Perform animation on this element.
6994          * @param {Object} args The YUI animation control args
6995          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
6996          * @param {Function} onComplete (optional) Function to call when animation completes
6997          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
6998          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
6999          * @return {Roo.Element} this
7000          */
7001         animate : function(args, duration, onComplete, easing, animType){
7002             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7003             return this;
7004         },
7005
7006         /*
7007          * @private Internal animation call
7008          */
7009         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7010             animType = animType || 'run';
7011             opt = opt || {};
7012             var anim = Roo.lib.Anim[animType](
7013                 this.dom, args,
7014                 (opt.duration || defaultDur) || .35,
7015                 (opt.easing || defaultEase) || 'easeOut',
7016                 function(){
7017                     Roo.callback(cb, this);
7018                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7019                 },
7020                 this
7021             );
7022             opt.anim = anim;
7023             return anim;
7024         },
7025
7026         // private legacy anim prep
7027         preanim : function(a, i){
7028             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7029         },
7030
7031         /**
7032          * Removes worthless text nodes
7033          * @param {Boolean} forceReclean (optional) By default the element
7034          * keeps track if it has been cleaned already so
7035          * you can call this over and over. However, if you update the element and
7036          * need to force a reclean, you can pass true.
7037          */
7038         clean : function(forceReclean){
7039             if(this.isCleaned && forceReclean !== true){
7040                 return this;
7041             }
7042             var ns = /\S/;
7043             var d = this.dom, n = d.firstChild, ni = -1;
7044             while(n){
7045                 var nx = n.nextSibling;
7046                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7047                     d.removeChild(n);
7048                 }else{
7049                     n.nodeIndex = ++ni;
7050                 }
7051                 n = nx;
7052             }
7053             this.isCleaned = true;
7054             return this;
7055         },
7056
7057         // private
7058         calcOffsetsTo : function(el){
7059             el = Roo.get(el);
7060             var d = el.dom;
7061             var restorePos = false;
7062             if(el.getStyle('position') == 'static'){
7063                 el.position('relative');
7064                 restorePos = true;
7065             }
7066             var x = 0, y =0;
7067             var op = this.dom;
7068             while(op && op != d && op.tagName != 'HTML'){
7069                 x+= op.offsetLeft;
7070                 y+= op.offsetTop;
7071                 op = op.offsetParent;
7072             }
7073             if(restorePos){
7074                 el.position('static');
7075             }
7076             return [x, y];
7077         },
7078
7079         /**
7080          * Scrolls this element into view within the passed container.
7081          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7082          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7083          * @return {Roo.Element} this
7084          */
7085         scrollIntoView : function(container, hscroll){
7086             var c = Roo.getDom(container) || document.body;
7087             var el = this.dom;
7088
7089             var o = this.calcOffsetsTo(c),
7090                 l = o[0],
7091                 t = o[1],
7092                 b = t+el.offsetHeight,
7093                 r = l+el.offsetWidth;
7094
7095             var ch = c.clientHeight;
7096             var ct = parseInt(c.scrollTop, 10);
7097             var cl = parseInt(c.scrollLeft, 10);
7098             var cb = ct + ch;
7099             var cr = cl + c.clientWidth;
7100
7101             if(t < ct){
7102                 c.scrollTop = t;
7103             }else if(b > cb){
7104                 c.scrollTop = b-ch;
7105             }
7106
7107             if(hscroll !== false){
7108                 if(l < cl){
7109                     c.scrollLeft = l;
7110                 }else if(r > cr){
7111                     c.scrollLeft = r-c.clientWidth;
7112                 }
7113             }
7114             return this;
7115         },
7116
7117         // private
7118         scrollChildIntoView : function(child, hscroll){
7119             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7120         },
7121
7122         /**
7123          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7124          * the new height may not be available immediately.
7125          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7126          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7127          * @param {Function} onComplete (optional) Function to call when animation completes
7128          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7129          * @return {Roo.Element} this
7130          */
7131         autoHeight : function(animate, duration, onComplete, easing){
7132             var oldHeight = this.getHeight();
7133             this.clip();
7134             this.setHeight(1); // force clipping
7135             setTimeout(function(){
7136                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7137                 if(!animate){
7138                     this.setHeight(height);
7139                     this.unclip();
7140                     if(typeof onComplete == "function"){
7141                         onComplete();
7142                     }
7143                 }else{
7144                     this.setHeight(oldHeight); // restore original height
7145                     this.setHeight(height, animate, duration, function(){
7146                         this.unclip();
7147                         if(typeof onComplete == "function") onComplete();
7148                     }.createDelegate(this), easing);
7149                 }
7150             }.createDelegate(this), 0);
7151             return this;
7152         },
7153
7154         /**
7155          * Returns true if this element is an ancestor of the passed element
7156          * @param {HTMLElement/String} el The element to check
7157          * @return {Boolean} True if this element is an ancestor of el, else false
7158          */
7159         contains : function(el){
7160             if(!el){return false;}
7161             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7162         },
7163
7164         /**
7165          * Checks whether the element is currently visible using both visibility and display properties.
7166          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7167          * @return {Boolean} True if the element is currently visible, else false
7168          */
7169         isVisible : function(deep) {
7170             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7171             if(deep !== true || !vis){
7172                 return vis;
7173             }
7174             var p = this.dom.parentNode;
7175             while(p && p.tagName.toLowerCase() != "body"){
7176                 if(!Roo.fly(p, '_isVisible').isVisible()){
7177                     return false;
7178                 }
7179                 p = p.parentNode;
7180             }
7181             return true;
7182         },
7183
7184         /**
7185          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7186          * @param {String} selector The CSS selector
7187          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7188          * @return {CompositeElement/CompositeElementLite} The composite element
7189          */
7190         select : function(selector, unique){
7191             return El.select(selector, unique, this.dom);
7192         },
7193
7194         /**
7195          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7196          * @param {String} selector The CSS selector
7197          * @return {Array} An array of the matched nodes
7198          */
7199         query : function(selector, unique){
7200             return Roo.DomQuery.select(selector, this.dom);
7201         },
7202
7203         /**
7204          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7205          * @param {String} selector The CSS selector
7206          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7207          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7208          */
7209         child : function(selector, returnDom){
7210             var n = Roo.DomQuery.selectNode(selector, this.dom);
7211             return returnDom ? n : Roo.get(n);
7212         },
7213
7214         /**
7215          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7216          * @param {String} selector The CSS selector
7217          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7218          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7219          */
7220         down : function(selector, returnDom){
7221             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7222             return returnDom ? n : Roo.get(n);
7223         },
7224
7225         /**
7226          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7227          * @param {String} group The group the DD object is member of
7228          * @param {Object} config The DD config object
7229          * @param {Object} overrides An object containing methods to override/implement on the DD object
7230          * @return {Roo.dd.DD} The DD object
7231          */
7232         initDD : function(group, config, overrides){
7233             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7234             return Roo.apply(dd, overrides);
7235         },
7236
7237         /**
7238          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7239          * @param {String} group The group the DDProxy object is member of
7240          * @param {Object} config The DDProxy config object
7241          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7242          * @return {Roo.dd.DDProxy} The DDProxy object
7243          */
7244         initDDProxy : function(group, config, overrides){
7245             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7246             return Roo.apply(dd, overrides);
7247         },
7248
7249         /**
7250          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7251          * @param {String} group The group the DDTarget object is member of
7252          * @param {Object} config The DDTarget config object
7253          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7254          * @return {Roo.dd.DDTarget} The DDTarget object
7255          */
7256         initDDTarget : function(group, config, overrides){
7257             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7258             return Roo.apply(dd, overrides);
7259         },
7260
7261         /**
7262          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7263          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7264          * @param {Boolean} visible Whether the element is visible
7265          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7266          * @return {Roo.Element} this
7267          */
7268          setVisible : function(visible, animate){
7269             if(!animate || !A){
7270                 if(this.visibilityMode == El.DISPLAY){
7271                     this.setDisplayed(visible);
7272                 }else{
7273                     this.fixDisplay();
7274                     this.dom.style.visibility = visible ? "visible" : "hidden";
7275                 }
7276             }else{
7277                 // closure for composites
7278                 var dom = this.dom;
7279                 var visMode = this.visibilityMode;
7280                 if(visible){
7281                     this.setOpacity(.01);
7282                     this.setVisible(true);
7283                 }
7284                 this.anim({opacity: { to: (visible?1:0) }},
7285                       this.preanim(arguments, 1),
7286                       null, .35, 'easeIn', function(){
7287                          if(!visible){
7288                              if(visMode == El.DISPLAY){
7289                                  dom.style.display = "none";
7290                              }else{
7291                                  dom.style.visibility = "hidden";
7292                              }
7293                              Roo.get(dom).setOpacity(1);
7294                          }
7295                      });
7296             }
7297             return this;
7298         },
7299
7300         /**
7301          * Returns true if display is not "none"
7302          * @return {Boolean}
7303          */
7304         isDisplayed : function() {
7305             return this.getStyle("display") != "none";
7306         },
7307
7308         /**
7309          * Toggles the element's visibility or display, depending on visibility mode.
7310          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7311          * @return {Roo.Element} this
7312          */
7313         toggle : function(animate){
7314             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7315             return this;
7316         },
7317
7318         /**
7319          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7320          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7321          * @return {Roo.Element} this
7322          */
7323         setDisplayed : function(value) {
7324             if(typeof value == "boolean"){
7325                value = value ? this.originalDisplay : "none";
7326             }
7327             this.setStyle("display", value);
7328             return this;
7329         },
7330
7331         /**
7332          * Tries to focus the element. Any exceptions are caught and ignored.
7333          * @return {Roo.Element} this
7334          */
7335         focus : function() {
7336             try{
7337                 this.dom.focus();
7338             }catch(e){}
7339             return this;
7340         },
7341
7342         /**
7343          * Tries to blur the element. Any exceptions are caught and ignored.
7344          * @return {Roo.Element} this
7345          */
7346         blur : function() {
7347             try{
7348                 this.dom.blur();
7349             }catch(e){}
7350             return this;
7351         },
7352
7353         /**
7354          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7355          * @param {String/Array} className The CSS class to add, or an array of classes
7356          * @return {Roo.Element} this
7357          */
7358         addClass : function(className){
7359             if(className instanceof Array){
7360                 for(var i = 0, len = className.length; i < len; i++) {
7361                     this.addClass(className[i]);
7362                 }
7363             }else{
7364                 if(className && !this.hasClass(className)){
7365                     this.dom.className = this.dom.className + " " + className;
7366                 }
7367             }
7368             return this;
7369         },
7370
7371         /**
7372          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7373          * @param {String/Array} className The CSS class to add, or an array of classes
7374          * @return {Roo.Element} this
7375          */
7376         radioClass : function(className){
7377             var siblings = this.dom.parentNode.childNodes;
7378             for(var i = 0; i < siblings.length; i++) {
7379                 var s = siblings[i];
7380                 if(s.nodeType == 1){
7381                     Roo.get(s).removeClass(className);
7382                 }
7383             }
7384             this.addClass(className);
7385             return this;
7386         },
7387
7388         /**
7389          * Removes one or more CSS classes from the element.
7390          * @param {String/Array} className The CSS class to remove, or an array of classes
7391          * @return {Roo.Element} this
7392          */
7393         removeClass : function(className){
7394             if(!className || !this.dom.className){
7395                 return this;
7396             }
7397             if(className instanceof Array){
7398                 for(var i = 0, len = className.length; i < len; i++) {
7399                     this.removeClass(className[i]);
7400                 }
7401             }else{
7402                 if(this.hasClass(className)){
7403                     var re = this.classReCache[className];
7404                     if (!re) {
7405                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7406                        this.classReCache[className] = re;
7407                     }
7408                     this.dom.className =
7409                         this.dom.className.replace(re, " ");
7410                 }
7411             }
7412             return this;
7413         },
7414
7415         // private
7416         classReCache: {},
7417
7418         /**
7419          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7420          * @param {String} className The CSS class to toggle
7421          * @return {Roo.Element} this
7422          */
7423         toggleClass : function(className){
7424             if(this.hasClass(className)){
7425                 this.removeClass(className);
7426             }else{
7427                 this.addClass(className);
7428             }
7429             return this;
7430         },
7431
7432         /**
7433          * Checks if the specified CSS class exists on this element's DOM node.
7434          * @param {String} className The CSS class to check for
7435          * @return {Boolean} True if the class exists, else false
7436          */
7437         hasClass : function(className){
7438             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7439         },
7440
7441         /**
7442          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7443          * @param {String} oldClassName The CSS class to replace
7444          * @param {String} newClassName The replacement CSS class
7445          * @return {Roo.Element} this
7446          */
7447         replaceClass : function(oldClassName, newClassName){
7448             this.removeClass(oldClassName);
7449             this.addClass(newClassName);
7450             return this;
7451         },
7452
7453         /**
7454          * Returns an object with properties matching the styles requested.
7455          * For example, el.getStyles('color', 'font-size', 'width') might return
7456          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7457          * @param {String} style1 A style name
7458          * @param {String} style2 A style name
7459          * @param {String} etc.
7460          * @return {Object} The style object
7461          */
7462         getStyles : function(){
7463             var a = arguments, len = a.length, r = {};
7464             for(var i = 0; i < len; i++){
7465                 r[a[i]] = this.getStyle(a[i]);
7466             }
7467             return r;
7468         },
7469
7470         /**
7471          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7472          * @param {String} property The style property whose value is returned.
7473          * @return {String} The current value of the style property for this element.
7474          */
7475         getStyle : function(){
7476             return view && view.getComputedStyle ?
7477                 function(prop){
7478                     var el = this.dom, v, cs, camel;
7479                     if(prop == 'float'){
7480                         prop = "cssFloat";
7481                     }
7482                     if(el.style && (v = el.style[prop])){
7483                         return v;
7484                     }
7485                     if(cs = view.getComputedStyle(el, "")){
7486                         if(!(camel = propCache[prop])){
7487                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7488                         }
7489                         return cs[camel];
7490                     }
7491                     return null;
7492                 } :
7493                 function(prop){
7494                     var el = this.dom, v, cs, camel;
7495                     if(prop == 'opacity'){
7496                         if(typeof el.style.filter == 'string'){
7497                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7498                             if(m){
7499                                 var fv = parseFloat(m[1]);
7500                                 if(!isNaN(fv)){
7501                                     return fv ? fv / 100 : 0;
7502                                 }
7503                             }
7504                         }
7505                         return 1;
7506                     }else if(prop == 'float'){
7507                         prop = "styleFloat";
7508                     }
7509                     if(!(camel = propCache[prop])){
7510                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7511                     }
7512                     if(v = el.style[camel]){
7513                         return v;
7514                     }
7515                     if(cs = el.currentStyle){
7516                         return cs[camel];
7517                     }
7518                     return null;
7519                 };
7520         }(),
7521
7522         /**
7523          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7524          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7525          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7526          * @return {Roo.Element} this
7527          */
7528         setStyle : function(prop, value){
7529             if(typeof prop == "string"){
7530                 
7531                 if (prop == 'float') {
7532                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7533                     return this;
7534                 }
7535                 
7536                 var camel;
7537                 if(!(camel = propCache[prop])){
7538                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7539                 }
7540                 
7541                 if(camel == 'opacity') {
7542                     this.setOpacity(value);
7543                 }else{
7544                     this.dom.style[camel] = value;
7545                 }
7546             }else{
7547                 for(var style in prop){
7548                     if(typeof prop[style] != "function"){
7549                        this.setStyle(style, prop[style]);
7550                     }
7551                 }
7552             }
7553             return this;
7554         },
7555
7556         /**
7557          * More flexible version of {@link #setStyle} for setting style properties.
7558          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7559          * a function which returns such a specification.
7560          * @return {Roo.Element} this
7561          */
7562         applyStyles : function(style){
7563             Roo.DomHelper.applyStyles(this.dom, style);
7564             return this;
7565         },
7566
7567         /**
7568           * 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).
7569           * @return {Number} The X position of the element
7570           */
7571         getX : function(){
7572             return D.getX(this.dom);
7573         },
7574
7575         /**
7576           * 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).
7577           * @return {Number} The Y position of the element
7578           */
7579         getY : function(){
7580             return D.getY(this.dom);
7581         },
7582
7583         /**
7584           * 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).
7585           * @return {Array} The XY position of the element
7586           */
7587         getXY : function(){
7588             return D.getXY(this.dom);
7589         },
7590
7591         /**
7592          * 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).
7593          * @param {Number} The X position of the element
7594          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7595          * @return {Roo.Element} this
7596          */
7597         setX : function(x, animate){
7598             if(!animate || !A){
7599                 D.setX(this.dom, x);
7600             }else{
7601                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7602             }
7603             return this;
7604         },
7605
7606         /**
7607          * 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).
7608          * @param {Number} The Y position of the element
7609          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7610          * @return {Roo.Element} this
7611          */
7612         setY : function(y, animate){
7613             if(!animate || !A){
7614                 D.setY(this.dom, y);
7615             }else{
7616                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7617             }
7618             return this;
7619         },
7620
7621         /**
7622          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7623          * @param {String} left The left CSS property value
7624          * @return {Roo.Element} this
7625          */
7626         setLeft : function(left){
7627             this.setStyle("left", this.addUnits(left));
7628             return this;
7629         },
7630
7631         /**
7632          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7633          * @param {String} top The top CSS property value
7634          * @return {Roo.Element} this
7635          */
7636         setTop : function(top){
7637             this.setStyle("top", this.addUnits(top));
7638             return this;
7639         },
7640
7641         /**
7642          * Sets the element's CSS right style.
7643          * @param {String} right The right CSS property value
7644          * @return {Roo.Element} this
7645          */
7646         setRight : function(right){
7647             this.setStyle("right", this.addUnits(right));
7648             return this;
7649         },
7650
7651         /**
7652          * Sets the element's CSS bottom style.
7653          * @param {String} bottom The bottom CSS property value
7654          * @return {Roo.Element} this
7655          */
7656         setBottom : function(bottom){
7657             this.setStyle("bottom", this.addUnits(bottom));
7658             return this;
7659         },
7660
7661         /**
7662          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7663          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7664          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7665          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7666          * @return {Roo.Element} this
7667          */
7668         setXY : function(pos, animate){
7669             if(!animate || !A){
7670                 D.setXY(this.dom, pos);
7671             }else{
7672                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7673             }
7674             return this;
7675         },
7676
7677         /**
7678          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7679          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7680          * @param {Number} x X value for new position (coordinates are page-based)
7681          * @param {Number} y Y value for new position (coordinates are page-based)
7682          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7683          * @return {Roo.Element} this
7684          */
7685         setLocation : function(x, y, animate){
7686             this.setXY([x, y], this.preanim(arguments, 2));
7687             return this;
7688         },
7689
7690         /**
7691          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7692          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7693          * @param {Number} x X value for new position (coordinates are page-based)
7694          * @param {Number} y Y value for new position (coordinates are page-based)
7695          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7696          * @return {Roo.Element} this
7697          */
7698         moveTo : function(x, y, animate){
7699             this.setXY([x, y], this.preanim(arguments, 2));
7700             return this;
7701         },
7702
7703         /**
7704          * Returns the region of the given element.
7705          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7706          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7707          */
7708         getRegion : function(){
7709             return D.getRegion(this.dom);
7710         },
7711
7712         /**
7713          * Returns the offset height of the element
7714          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7715          * @return {Number} The element's height
7716          */
7717         getHeight : function(contentHeight){
7718             var h = this.dom.offsetHeight || 0;
7719             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7720         },
7721
7722         /**
7723          * Returns the offset width of the element
7724          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7725          * @return {Number} The element's width
7726          */
7727         getWidth : function(contentWidth){
7728             var w = this.dom.offsetWidth || 0;
7729             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7730         },
7731
7732         /**
7733          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7734          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7735          * if a height has not been set using CSS.
7736          * @return {Number}
7737          */
7738         getComputedHeight : function(){
7739             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7740             if(!h){
7741                 h = parseInt(this.getStyle('height'), 10) || 0;
7742                 if(!this.isBorderBox()){
7743                     h += this.getFrameWidth('tb');
7744                 }
7745             }
7746             return h;
7747         },
7748
7749         /**
7750          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7751          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7752          * if a width has not been set using CSS.
7753          * @return {Number}
7754          */
7755         getComputedWidth : function(){
7756             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
7757             if(!w){
7758                 w = parseInt(this.getStyle('width'), 10) || 0;
7759                 if(!this.isBorderBox()){
7760                     w += this.getFrameWidth('lr');
7761                 }
7762             }
7763             return w;
7764         },
7765
7766         /**
7767          * Returns the size of the element.
7768          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
7769          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
7770          */
7771         getSize : function(contentSize){
7772             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
7773         },
7774
7775         /**
7776          * Returns the width and height of the viewport.
7777          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
7778          */
7779         getViewSize : function(){
7780             var d = this.dom, doc = document, aw = 0, ah = 0;
7781             if(d == doc || d == doc.body){
7782                 return {width : D.getViewWidth(), height: D.getViewHeight()};
7783             }else{
7784                 return {
7785                     width : d.clientWidth,
7786                     height: d.clientHeight
7787                 };
7788             }
7789         },
7790
7791         /**
7792          * Returns the value of the "value" attribute
7793          * @param {Boolean} asNumber true to parse the value as a number
7794          * @return {String/Number}
7795          */
7796         getValue : function(asNumber){
7797             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
7798         },
7799
7800         // private
7801         adjustWidth : function(width){
7802             if(typeof width == "number"){
7803                 if(this.autoBoxAdjust && !this.isBorderBox()){
7804                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
7805                 }
7806                 if(width < 0){
7807                     width = 0;
7808                 }
7809             }
7810             return width;
7811         },
7812
7813         // private
7814         adjustHeight : function(height){
7815             if(typeof height == "number"){
7816                if(this.autoBoxAdjust && !this.isBorderBox()){
7817                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
7818                }
7819                if(height < 0){
7820                    height = 0;
7821                }
7822             }
7823             return height;
7824         },
7825
7826         /**
7827          * Set the width of the element
7828          * @param {Number} width The new width
7829          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7830          * @return {Roo.Element} this
7831          */
7832         setWidth : function(width, animate){
7833             width = this.adjustWidth(width);
7834             if(!animate || !A){
7835                 this.dom.style.width = this.addUnits(width);
7836             }else{
7837                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
7838             }
7839             return this;
7840         },
7841
7842         /**
7843          * Set the height of the element
7844          * @param {Number} height The new height
7845          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7846          * @return {Roo.Element} this
7847          */
7848          setHeight : function(height, animate){
7849             height = this.adjustHeight(height);
7850             if(!animate || !A){
7851                 this.dom.style.height = this.addUnits(height);
7852             }else{
7853                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
7854             }
7855             return this;
7856         },
7857
7858         /**
7859          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
7860          * @param {Number} width The new width
7861          * @param {Number} height The new height
7862          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7863          * @return {Roo.Element} this
7864          */
7865          setSize : function(width, height, animate){
7866             if(typeof width == "object"){ // in case of object from getSize()
7867                 height = width.height; width = width.width;
7868             }
7869             width = this.adjustWidth(width); height = this.adjustHeight(height);
7870             if(!animate || !A){
7871                 this.dom.style.width = this.addUnits(width);
7872                 this.dom.style.height = this.addUnits(height);
7873             }else{
7874                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
7875             }
7876             return this;
7877         },
7878
7879         /**
7880          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
7881          * @param {Number} x X value for new position (coordinates are page-based)
7882          * @param {Number} y Y value for new position (coordinates are page-based)
7883          * @param {Number} width The new width
7884          * @param {Number} height The new height
7885          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7886          * @return {Roo.Element} this
7887          */
7888         setBounds : function(x, y, width, height, animate){
7889             if(!animate || !A){
7890                 this.setSize(width, height);
7891                 this.setLocation(x, y);
7892             }else{
7893                 width = this.adjustWidth(width); height = this.adjustHeight(height);
7894                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
7895                               this.preanim(arguments, 4), 'motion');
7896             }
7897             return this;
7898         },
7899
7900         /**
7901          * 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.
7902          * @param {Roo.lib.Region} region The region to fill
7903          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7904          * @return {Roo.Element} this
7905          */
7906         setRegion : function(region, animate){
7907             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
7908             return this;
7909         },
7910
7911         /**
7912          * Appends an event handler
7913          *
7914          * @param {String}   eventName     The type of event to append
7915          * @param {Function} fn        The method the event invokes
7916          * @param {Object} scope       (optional) The scope (this object) of the fn
7917          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
7918          */
7919         addListener : function(eventName, fn, scope, options){
7920             if (this.dom) {
7921                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
7922             }
7923         },
7924
7925         /**
7926          * Removes an event handler from this element
7927          * @param {String} eventName the type of event to remove
7928          * @param {Function} fn the method the event invokes
7929          * @return {Roo.Element} this
7930          */
7931         removeListener : function(eventName, fn){
7932             Roo.EventManager.removeListener(this.dom,  eventName, fn);
7933             return this;
7934         },
7935
7936         /**
7937          * Removes all previous added listeners from this element
7938          * @return {Roo.Element} this
7939          */
7940         removeAllListeners : function(){
7941             E.purgeElement(this.dom);
7942             return this;
7943         },
7944
7945         relayEvent : function(eventName, observable){
7946             this.on(eventName, function(e){
7947                 observable.fireEvent(eventName, e);
7948             });
7949         },
7950
7951         /**
7952          * Set the opacity of the element
7953          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
7954          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7955          * @return {Roo.Element} this
7956          */
7957          setOpacity : function(opacity, animate){
7958             if(!animate || !A){
7959                 var s = this.dom.style;
7960                 if(Roo.isIE){
7961                     s.zoom = 1;
7962                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
7963                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
7964                 }else{
7965                     s.opacity = opacity;
7966                 }
7967             }else{
7968                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
7969             }
7970             return this;
7971         },
7972
7973         /**
7974          * Gets the left X coordinate
7975          * @param {Boolean} local True to get the local css position instead of page coordinate
7976          * @return {Number}
7977          */
7978         getLeft : function(local){
7979             if(!local){
7980                 return this.getX();
7981             }else{
7982                 return parseInt(this.getStyle("left"), 10) || 0;
7983             }
7984         },
7985
7986         /**
7987          * Gets the right X coordinate of the element (element X position + element width)
7988          * @param {Boolean} local True to get the local css position instead of page coordinate
7989          * @return {Number}
7990          */
7991         getRight : function(local){
7992             if(!local){
7993                 return this.getX() + this.getWidth();
7994             }else{
7995                 return (this.getLeft(true) + this.getWidth()) || 0;
7996             }
7997         },
7998
7999         /**
8000          * Gets the top Y coordinate
8001          * @param {Boolean} local True to get the local css position instead of page coordinate
8002          * @return {Number}
8003          */
8004         getTop : function(local) {
8005             if(!local){
8006                 return this.getY();
8007             }else{
8008                 return parseInt(this.getStyle("top"), 10) || 0;
8009             }
8010         },
8011
8012         /**
8013          * Gets the bottom Y coordinate of the element (element Y position + element height)
8014          * @param {Boolean} local True to get the local css position instead of page coordinate
8015          * @return {Number}
8016          */
8017         getBottom : function(local){
8018             if(!local){
8019                 return this.getY() + this.getHeight();
8020             }else{
8021                 return (this.getTop(true) + this.getHeight()) || 0;
8022             }
8023         },
8024
8025         /**
8026         * Initializes positioning on this element. If a desired position is not passed, it will make the
8027         * the element positioned relative IF it is not already positioned.
8028         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8029         * @param {Number} zIndex (optional) The zIndex to apply
8030         * @param {Number} x (optional) Set the page X position
8031         * @param {Number} y (optional) Set the page Y position
8032         */
8033         position : function(pos, zIndex, x, y){
8034             if(!pos){
8035                if(this.getStyle('position') == 'static'){
8036                    this.setStyle('position', 'relative');
8037                }
8038             }else{
8039                 this.setStyle("position", pos);
8040             }
8041             if(zIndex){
8042                 this.setStyle("z-index", zIndex);
8043             }
8044             if(x !== undefined && y !== undefined){
8045                 this.setXY([x, y]);
8046             }else if(x !== undefined){
8047                 this.setX(x);
8048             }else if(y !== undefined){
8049                 this.setY(y);
8050             }
8051         },
8052
8053         /**
8054         * Clear positioning back to the default when the document was loaded
8055         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8056         * @return {Roo.Element} this
8057          */
8058         clearPositioning : function(value){
8059             value = value ||'';
8060             this.setStyle({
8061                 "left": value,
8062                 "right": value,
8063                 "top": value,
8064                 "bottom": value,
8065                 "z-index": "",
8066                 "position" : "static"
8067             });
8068             return this;
8069         },
8070
8071         /**
8072         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8073         * snapshot before performing an update and then restoring the element.
8074         * @return {Object}
8075         */
8076         getPositioning : function(){
8077             var l = this.getStyle("left");
8078             var t = this.getStyle("top");
8079             return {
8080                 "position" : this.getStyle("position"),
8081                 "left" : l,
8082                 "right" : l ? "" : this.getStyle("right"),
8083                 "top" : t,
8084                 "bottom" : t ? "" : this.getStyle("bottom"),
8085                 "z-index" : this.getStyle("z-index")
8086             };
8087         },
8088
8089         /**
8090          * Gets the width of the border(s) for the specified side(s)
8091          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8092          * passing lr would get the border (l)eft width + the border (r)ight width.
8093          * @return {Number} The width of the sides passed added together
8094          */
8095         getBorderWidth : function(side){
8096             return this.addStyles(side, El.borders);
8097         },
8098
8099         /**
8100          * Gets the width of the padding(s) for the specified side(s)
8101          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8102          * passing lr would get the padding (l)eft + the padding (r)ight.
8103          * @return {Number} The padding of the sides passed added together
8104          */
8105         getPadding : function(side){
8106             return this.addStyles(side, El.paddings);
8107         },
8108
8109         /**
8110         * Set positioning with an object returned by getPositioning().
8111         * @param {Object} posCfg
8112         * @return {Roo.Element} this
8113          */
8114         setPositioning : function(pc){
8115             this.applyStyles(pc);
8116             if(pc.right == "auto"){
8117                 this.dom.style.right = "";
8118             }
8119             if(pc.bottom == "auto"){
8120                 this.dom.style.bottom = "";
8121             }
8122             return this;
8123         },
8124
8125         // private
8126         fixDisplay : function(){
8127             if(this.getStyle("display") == "none"){
8128                 this.setStyle("visibility", "hidden");
8129                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8130                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8131                     this.setStyle("display", "block");
8132                 }
8133             }
8134         },
8135
8136         /**
8137          * Quick set left and top adding default units
8138          * @param {String} left The left CSS property value
8139          * @param {String} top The top CSS property value
8140          * @return {Roo.Element} this
8141          */
8142          setLeftTop : function(left, top){
8143             this.dom.style.left = this.addUnits(left);
8144             this.dom.style.top = this.addUnits(top);
8145             return this;
8146         },
8147
8148         /**
8149          * Move this element relative to its current position.
8150          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8151          * @param {Number} distance How far to move the element in pixels
8152          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8153          * @return {Roo.Element} this
8154          */
8155          move : function(direction, distance, animate){
8156             var xy = this.getXY();
8157             direction = direction.toLowerCase();
8158             switch(direction){
8159                 case "l":
8160                 case "left":
8161                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8162                     break;
8163                case "r":
8164                case "right":
8165                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8166                     break;
8167                case "t":
8168                case "top":
8169                case "up":
8170                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8171                     break;
8172                case "b":
8173                case "bottom":
8174                case "down":
8175                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8176                     break;
8177             }
8178             return this;
8179         },
8180
8181         /**
8182          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8183          * @return {Roo.Element} this
8184          */
8185         clip : function(){
8186             if(!this.isClipped){
8187                this.isClipped = true;
8188                this.originalClip = {
8189                    "o": this.getStyle("overflow"),
8190                    "x": this.getStyle("overflow-x"),
8191                    "y": this.getStyle("overflow-y")
8192                };
8193                this.setStyle("overflow", "hidden");
8194                this.setStyle("overflow-x", "hidden");
8195                this.setStyle("overflow-y", "hidden");
8196             }
8197             return this;
8198         },
8199
8200         /**
8201          *  Return clipping (overflow) to original clipping before clip() was called
8202          * @return {Roo.Element} this
8203          */
8204         unclip : function(){
8205             if(this.isClipped){
8206                 this.isClipped = false;
8207                 var o = this.originalClip;
8208                 if(o.o){this.setStyle("overflow", o.o);}
8209                 if(o.x){this.setStyle("overflow-x", o.x);}
8210                 if(o.y){this.setStyle("overflow-y", o.y);}
8211             }
8212             return this;
8213         },
8214
8215
8216         /**
8217          * Gets the x,y coordinates specified by the anchor position on the element.
8218          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8219          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8220          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8221          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8222          * @return {Array} [x, y] An array containing the element's x and y coordinates
8223          */
8224         getAnchorXY : function(anchor, local, s){
8225             //Passing a different size is useful for pre-calculating anchors,
8226             //especially for anchored animations that change the el size.
8227
8228             var w, h, vp = false;
8229             if(!s){
8230                 var d = this.dom;
8231                 if(d == document.body || d == document){
8232                     vp = true;
8233                     w = D.getViewWidth(); h = D.getViewHeight();
8234                 }else{
8235                     w = this.getWidth(); h = this.getHeight();
8236                 }
8237             }else{
8238                 w = s.width;  h = s.height;
8239             }
8240             var x = 0, y = 0, r = Math.round;
8241             switch((anchor || "tl").toLowerCase()){
8242                 case "c":
8243                     x = r(w*.5);
8244                     y = r(h*.5);
8245                 break;
8246                 case "t":
8247                     x = r(w*.5);
8248                     y = 0;
8249                 break;
8250                 case "l":
8251                     x = 0;
8252                     y = r(h*.5);
8253                 break;
8254                 case "r":
8255                     x = w;
8256                     y = r(h*.5);
8257                 break;
8258                 case "b":
8259                     x = r(w*.5);
8260                     y = h;
8261                 break;
8262                 case "tl":
8263                     x = 0;
8264                     y = 0;
8265                 break;
8266                 case "bl":
8267                     x = 0;
8268                     y = h;
8269                 break;
8270                 case "br":
8271                     x = w;
8272                     y = h;
8273                 break;
8274                 case "tr":
8275                     x = w;
8276                     y = 0;
8277                 break;
8278             }
8279             if(local === true){
8280                 return [x, y];
8281             }
8282             if(vp){
8283                 var sc = this.getScroll();
8284                 return [x + sc.left, y + sc.top];
8285             }
8286             //Add the element's offset xy
8287             var o = this.getXY();
8288             return [x+o[0], y+o[1]];
8289         },
8290
8291         /**
8292          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8293          * supported position values.
8294          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8295          * @param {String} position The position to align to.
8296          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8297          * @return {Array} [x, y]
8298          */
8299         getAlignToXY : function(el, p, o){
8300             el = Roo.get(el);
8301             var d = this.dom;
8302             if(!el.dom){
8303                 throw "Element.alignTo with an element that doesn't exist";
8304             }
8305             var c = false; //constrain to viewport
8306             var p1 = "", p2 = "";
8307             o = o || [0,0];
8308
8309             if(!p){
8310                 p = "tl-bl";
8311             }else if(p == "?"){
8312                 p = "tl-bl?";
8313             }else if(p.indexOf("-") == -1){
8314                 p = "tl-" + p;
8315             }
8316             p = p.toLowerCase();
8317             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8318             if(!m){
8319                throw "Element.alignTo with an invalid alignment " + p;
8320             }
8321             p1 = m[1]; p2 = m[2]; c = !!m[3];
8322
8323             //Subtract the aligned el's internal xy from the target's offset xy
8324             //plus custom offset to get the aligned el's new offset xy
8325             var a1 = this.getAnchorXY(p1, true);
8326             var a2 = el.getAnchorXY(p2, false);
8327             var x = a2[0] - a1[0] + o[0];
8328             var y = a2[1] - a1[1] + o[1];
8329             if(c){
8330                 //constrain the aligned el to viewport if necessary
8331                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8332                 // 5px of margin for ie
8333                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8334
8335                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8336                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8337                 //otherwise swap the aligned el to the opposite border of the target.
8338                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8339                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8340                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8341                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8342
8343                var doc = document;
8344                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8345                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8346
8347                if((x+w) > dw + scrollX){
8348                     x = swapX ? r.left-w : dw+scrollX-w;
8349                 }
8350                if(x < scrollX){
8351                    x = swapX ? r.right : scrollX;
8352                }
8353                if((y+h) > dh + scrollY){
8354                     y = swapY ? r.top-h : dh+scrollY-h;
8355                 }
8356                if (y < scrollY){
8357                    y = swapY ? r.bottom : scrollY;
8358                }
8359             }
8360             return [x,y];
8361         },
8362
8363         // private
8364         getConstrainToXY : function(){
8365             var os = {top:0, left:0, bottom:0, right: 0};
8366
8367             return function(el, local, offsets, proposedXY){
8368                 el = Roo.get(el);
8369                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8370
8371                 var vw, vh, vx = 0, vy = 0;
8372                 if(el.dom == document.body || el.dom == document){
8373                     vw = Roo.lib.Dom.getViewWidth();
8374                     vh = Roo.lib.Dom.getViewHeight();
8375                 }else{
8376                     vw = el.dom.clientWidth;
8377                     vh = el.dom.clientHeight;
8378                     if(!local){
8379                         var vxy = el.getXY();
8380                         vx = vxy[0];
8381                         vy = vxy[1];
8382                     }
8383                 }
8384
8385                 var s = el.getScroll();
8386
8387                 vx += offsets.left + s.left;
8388                 vy += offsets.top + s.top;
8389
8390                 vw -= offsets.right;
8391                 vh -= offsets.bottom;
8392
8393                 var vr = vx+vw;
8394                 var vb = vy+vh;
8395
8396                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8397                 var x = xy[0], y = xy[1];
8398                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8399
8400                 // only move it if it needs it
8401                 var moved = false;
8402
8403                 // first validate right/bottom
8404                 if((x + w) > vr){
8405                     x = vr - w;
8406                     moved = true;
8407                 }
8408                 if((y + h) > vb){
8409                     y = vb - h;
8410                     moved = true;
8411                 }
8412                 // then make sure top/left isn't negative
8413                 if(x < vx){
8414                     x = vx;
8415                     moved = true;
8416                 }
8417                 if(y < vy){
8418                     y = vy;
8419                     moved = true;
8420                 }
8421                 return moved ? [x, y] : false;
8422             };
8423         }(),
8424
8425         // private
8426         adjustForConstraints : function(xy, parent, offsets){
8427             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8428         },
8429
8430         /**
8431          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8432          * document it aligns it to the viewport.
8433          * The position parameter is optional, and can be specified in any one of the following formats:
8434          * <ul>
8435          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8436          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8437          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8438          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8439          *   <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
8440          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8441          * </ul>
8442          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8443          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8444          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8445          * that specified in order to enforce the viewport constraints.
8446          * Following are all of the supported anchor positions:
8447     <pre>
8448     Value  Description
8449     -----  -----------------------------
8450     tl     The top left corner (default)
8451     t      The center of the top edge
8452     tr     The top right corner
8453     l      The center of the left edge
8454     c      In the center of the element
8455     r      The center of the right edge
8456     bl     The bottom left corner
8457     b      The center of the bottom edge
8458     br     The bottom right corner
8459     </pre>
8460     Example Usage:
8461     <pre><code>
8462     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8463     el.alignTo("other-el");
8464
8465     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8466     el.alignTo("other-el", "tr?");
8467
8468     // align the bottom right corner of el with the center left edge of other-el
8469     el.alignTo("other-el", "br-l?");
8470
8471     // align the center of el with the bottom left corner of other-el and
8472     // adjust the x position by -6 pixels (and the y position by 0)
8473     el.alignTo("other-el", "c-bl", [-6, 0]);
8474     </code></pre>
8475          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8476          * @param {String} position The position to align to.
8477          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8478          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8479          * @return {Roo.Element} this
8480          */
8481         alignTo : function(element, position, offsets, animate){
8482             var xy = this.getAlignToXY(element, position, offsets);
8483             this.setXY(xy, this.preanim(arguments, 3));
8484             return this;
8485         },
8486
8487         /**
8488          * Anchors an element to another element and realigns it when the window is resized.
8489          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8490          * @param {String} position The position to align to.
8491          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8492          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8493          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8494          * is a number, it is used as the buffer delay (defaults to 50ms).
8495          * @param {Function} callback The function to call after the animation finishes
8496          * @return {Roo.Element} this
8497          */
8498         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8499             var action = function(){
8500                 this.alignTo(el, alignment, offsets, animate);
8501                 Roo.callback(callback, this);
8502             };
8503             Roo.EventManager.onWindowResize(action, this);
8504             var tm = typeof monitorScroll;
8505             if(tm != 'undefined'){
8506                 Roo.EventManager.on(window, 'scroll', action, this,
8507                     {buffer: tm == 'number' ? monitorScroll : 50});
8508             }
8509             action.call(this); // align immediately
8510             return this;
8511         },
8512         /**
8513          * Clears any opacity settings from this element. Required in some cases for IE.
8514          * @return {Roo.Element} this
8515          */
8516         clearOpacity : function(){
8517             if (window.ActiveXObject) {
8518                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8519                     this.dom.style.filter = "";
8520                 }
8521             } else {
8522                 this.dom.style.opacity = "";
8523                 this.dom.style["-moz-opacity"] = "";
8524                 this.dom.style["-khtml-opacity"] = "";
8525             }
8526             return this;
8527         },
8528
8529         /**
8530          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8531          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8532          * @return {Roo.Element} this
8533          */
8534         hide : function(animate){
8535             this.setVisible(false, this.preanim(arguments, 0));
8536             return this;
8537         },
8538
8539         /**
8540         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8541         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8542          * @return {Roo.Element} this
8543          */
8544         show : function(animate){
8545             this.setVisible(true, this.preanim(arguments, 0));
8546             return this;
8547         },
8548
8549         /**
8550          * @private Test if size has a unit, otherwise appends the default
8551          */
8552         addUnits : function(size){
8553             return Roo.Element.addUnits(size, this.defaultUnit);
8554         },
8555
8556         /**
8557          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8558          * @return {Roo.Element} this
8559          */
8560         beginMeasure : function(){
8561             var el = this.dom;
8562             if(el.offsetWidth || el.offsetHeight){
8563                 return this; // offsets work already
8564             }
8565             var changed = [];
8566             var p = this.dom, b = document.body; // start with this element
8567             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8568                 var pe = Roo.get(p);
8569                 if(pe.getStyle('display') == 'none'){
8570                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8571                     p.style.visibility = "hidden";
8572                     p.style.display = "block";
8573                 }
8574                 p = p.parentNode;
8575             }
8576             this._measureChanged = changed;
8577             return this;
8578
8579         },
8580
8581         /**
8582          * Restores displays to before beginMeasure was called
8583          * @return {Roo.Element} this
8584          */
8585         endMeasure : function(){
8586             var changed = this._measureChanged;
8587             if(changed){
8588                 for(var i = 0, len = changed.length; i < len; i++) {
8589                     var r = changed[i];
8590                     r.el.style.visibility = r.visibility;
8591                     r.el.style.display = "none";
8592                 }
8593                 this._measureChanged = null;
8594             }
8595             return this;
8596         },
8597
8598         /**
8599         * Update the innerHTML of this element, optionally searching for and processing scripts
8600         * @param {String} html The new HTML
8601         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8602         * @param {Function} callback For async script loading you can be noticed when the update completes
8603         * @return {Roo.Element} this
8604          */
8605         update : function(html, loadScripts, callback){
8606             if(typeof html == "undefined"){
8607                 html = "";
8608             }
8609             if(loadScripts !== true){
8610                 this.dom.innerHTML = html;
8611                 if(typeof callback == "function"){
8612                     callback();
8613                 }
8614                 return this;
8615             }
8616             var id = Roo.id();
8617             var dom = this.dom;
8618
8619             html += '<span id="' + id + '"></span>';
8620
8621             E.onAvailable(id, function(){
8622                 var hd = document.getElementsByTagName("head")[0];
8623                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8624                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8625                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8626
8627                 var match;
8628                 while(match = re.exec(html)){
8629                     var attrs = match[1];
8630                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8631                     if(srcMatch && srcMatch[2]){
8632                        var s = document.createElement("script");
8633                        s.src = srcMatch[2];
8634                        var typeMatch = attrs.match(typeRe);
8635                        if(typeMatch && typeMatch[2]){
8636                            s.type = typeMatch[2];
8637                        }
8638                        hd.appendChild(s);
8639                     }else if(match[2] && match[2].length > 0){
8640                         if(window.execScript) {
8641                            window.execScript(match[2]);
8642                         } else {
8643                             /**
8644                              * eval:var:id
8645                              * eval:var:dom
8646                              * eval:var:html
8647                              * 
8648                              */
8649                            window.eval(match[2]);
8650                         }
8651                     }
8652                 }
8653                 var el = document.getElementById(id);
8654                 if(el){el.parentNode.removeChild(el);}
8655                 if(typeof callback == "function"){
8656                     callback();
8657                 }
8658             });
8659             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8660             return this;
8661         },
8662
8663         /**
8664          * Direct access to the UpdateManager update() method (takes the same parameters).
8665          * @param {String/Function} url The url for this request or a function to call to get the url
8666          * @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}
8667          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8668          * @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.
8669          * @return {Roo.Element} this
8670          */
8671         load : function(){
8672             var um = this.getUpdateManager();
8673             um.update.apply(um, arguments);
8674             return this;
8675         },
8676
8677         /**
8678         * Gets this element's UpdateManager
8679         * @return {Roo.UpdateManager} The UpdateManager
8680         */
8681         getUpdateManager : function(){
8682             if(!this.updateManager){
8683                 this.updateManager = new Roo.UpdateManager(this);
8684             }
8685             return this.updateManager;
8686         },
8687
8688         /**
8689          * Disables text selection for this element (normalized across browsers)
8690          * @return {Roo.Element} this
8691          */
8692         unselectable : function(){
8693             this.dom.unselectable = "on";
8694             this.swallowEvent("selectstart", true);
8695             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8696             this.addClass("x-unselectable");
8697             return this;
8698         },
8699
8700         /**
8701         * Calculates the x, y to center this element on the screen
8702         * @return {Array} The x, y values [x, y]
8703         */
8704         getCenterXY : function(){
8705             return this.getAlignToXY(document, 'c-c');
8706         },
8707
8708         /**
8709         * Centers the Element in either the viewport, or another Element.
8710         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8711         */
8712         center : function(centerIn){
8713             this.alignTo(centerIn || document, 'c-c');
8714             return this;
8715         },
8716
8717         /**
8718          * Tests various css rules/browsers to determine if this element uses a border box
8719          * @return {Boolean}
8720          */
8721         isBorderBox : function(){
8722             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8723         },
8724
8725         /**
8726          * Return a box {x, y, width, height} that can be used to set another elements
8727          * size/location to match this element.
8728          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8729          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8730          * @return {Object} box An object in the format {x, y, width, height}
8731          */
8732         getBox : function(contentBox, local){
8733             var xy;
8734             if(!local){
8735                 xy = this.getXY();
8736             }else{
8737                 var left = parseInt(this.getStyle("left"), 10) || 0;
8738                 var top = parseInt(this.getStyle("top"), 10) || 0;
8739                 xy = [left, top];
8740             }
8741             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8742             if(!contentBox){
8743                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8744             }else{
8745                 var l = this.getBorderWidth("l")+this.getPadding("l");
8746                 var r = this.getBorderWidth("r")+this.getPadding("r");
8747                 var t = this.getBorderWidth("t")+this.getPadding("t");
8748                 var b = this.getBorderWidth("b")+this.getPadding("b");
8749                 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)};
8750             }
8751             bx.right = bx.x + bx.width;
8752             bx.bottom = bx.y + bx.height;
8753             return bx;
8754         },
8755
8756         /**
8757          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
8758          for more information about the sides.
8759          * @param {String} sides
8760          * @return {Number}
8761          */
8762         getFrameWidth : function(sides, onlyContentBox){
8763             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
8764         },
8765
8766         /**
8767          * 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.
8768          * @param {Object} box The box to fill {x, y, width, height}
8769          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
8770          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8771          * @return {Roo.Element} this
8772          */
8773         setBox : function(box, adjust, animate){
8774             var w = box.width, h = box.height;
8775             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
8776                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8777                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8778             }
8779             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
8780             return this;
8781         },
8782
8783         /**
8784          * Forces the browser to repaint this element
8785          * @return {Roo.Element} this
8786          */
8787          repaint : function(){
8788             var dom = this.dom;
8789             this.addClass("x-repaint");
8790             setTimeout(function(){
8791                 Roo.get(dom).removeClass("x-repaint");
8792             }, 1);
8793             return this;
8794         },
8795
8796         /**
8797          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
8798          * then it returns the calculated width of the sides (see getPadding)
8799          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
8800          * @return {Object/Number}
8801          */
8802         getMargins : function(side){
8803             if(!side){
8804                 return {
8805                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
8806                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
8807                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
8808                     right: parseInt(this.getStyle("margin-right"), 10) || 0
8809                 };
8810             }else{
8811                 return this.addStyles(side, El.margins);
8812              }
8813         },
8814
8815         // private
8816         addStyles : function(sides, styles){
8817             var val = 0, v, w;
8818             for(var i = 0, len = sides.length; i < len; i++){
8819                 v = this.getStyle(styles[sides.charAt(i)]);
8820                 if(v){
8821                      w = parseInt(v, 10);
8822                      if(w){ val += w; }
8823                 }
8824             }
8825             return val;
8826         },
8827
8828         /**
8829          * Creates a proxy element of this element
8830          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
8831          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
8832          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
8833          * @return {Roo.Element} The new proxy element
8834          */
8835         createProxy : function(config, renderTo, matchBox){
8836             if(renderTo){
8837                 renderTo = Roo.getDom(renderTo);
8838             }else{
8839                 renderTo = document.body;
8840             }
8841             config = typeof config == "object" ?
8842                 config : {tag : "div", cls: config};
8843             var proxy = Roo.DomHelper.append(renderTo, config, true);
8844             if(matchBox){
8845                proxy.setBox(this.getBox());
8846             }
8847             return proxy;
8848         },
8849
8850         /**
8851          * Puts a mask over this element to disable user interaction. Requires core.css.
8852          * This method can only be applied to elements which accept child nodes.
8853          * @param {String} msg (optional) A message to display in the mask
8854          * @param {String} msgCls (optional) A css class to apply to the msg element
8855          * @return {Element} The mask  element
8856          */
8857         mask : function(msg, msgCls){
8858             if(this.getStyle("position") == "static"){
8859                 this.setStyle("position", "relative");
8860             }
8861             if(!this._mask){
8862                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
8863             }
8864             this.addClass("x-masked");
8865             this._mask.setDisplayed(true);
8866             if(typeof msg == 'string'){
8867                 if(!this._maskMsg){
8868                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
8869                 }
8870                 var mm = this._maskMsg;
8871                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
8872                 mm.dom.firstChild.innerHTML = msg;
8873                 mm.setDisplayed(true);
8874                 mm.center(this);
8875             }
8876             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
8877                 this._mask.setHeight(this.getHeight());
8878             }
8879             return this._mask;
8880         },
8881
8882         /**
8883          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
8884          * it is cached for reuse.
8885          */
8886         unmask : function(removeEl){
8887             if(this._mask){
8888                 if(removeEl === true){
8889                     this._mask.remove();
8890                     delete this._mask;
8891                     if(this._maskMsg){
8892                         this._maskMsg.remove();
8893                         delete this._maskMsg;
8894                     }
8895                 }else{
8896                     this._mask.setDisplayed(false);
8897                     if(this._maskMsg){
8898                         this._maskMsg.setDisplayed(false);
8899                     }
8900                 }
8901             }
8902             this.removeClass("x-masked");
8903         },
8904
8905         /**
8906          * Returns true if this element is masked
8907          * @return {Boolean}
8908          */
8909         isMasked : function(){
8910             return this._mask && this._mask.isVisible();
8911         },
8912
8913         /**
8914          * Creates an iframe shim for this element to keep selects and other windowed objects from
8915          * showing through.
8916          * @return {Roo.Element} The new shim element
8917          */
8918         createShim : function(){
8919             var el = document.createElement('iframe');
8920             el.frameBorder = 'no';
8921             el.className = 'roo-shim';
8922             if(Roo.isIE && Roo.isSecure){
8923                 el.src = Roo.SSL_SECURE_URL;
8924             }
8925             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
8926             shim.autoBoxAdjust = false;
8927             return shim;
8928         },
8929
8930         /**
8931          * Removes this element from the DOM and deletes it from the cache
8932          */
8933         remove : function(){
8934             if(this.dom.parentNode){
8935                 this.dom.parentNode.removeChild(this.dom);
8936             }
8937             delete El.cache[this.dom.id];
8938         },
8939
8940         /**
8941          * Sets up event handlers to add and remove a css class when the mouse is over this element
8942          * @param {String} className
8943          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
8944          * mouseout events for children elements
8945          * @return {Roo.Element} this
8946          */
8947         addClassOnOver : function(className, preventFlicker){
8948             this.on("mouseover", function(){
8949                 Roo.fly(this, '_internal').addClass(className);
8950             }, this.dom);
8951             var removeFn = function(e){
8952                 if(preventFlicker !== true || !e.within(this, true)){
8953                     Roo.fly(this, '_internal').removeClass(className);
8954                 }
8955             };
8956             this.on("mouseout", removeFn, this.dom);
8957             return this;
8958         },
8959
8960         /**
8961          * Sets up event handlers to add and remove a css class when this element has the focus
8962          * @param {String} className
8963          * @return {Roo.Element} this
8964          */
8965         addClassOnFocus : function(className){
8966             this.on("focus", function(){
8967                 Roo.fly(this, '_internal').addClass(className);
8968             }, this.dom);
8969             this.on("blur", function(){
8970                 Roo.fly(this, '_internal').removeClass(className);
8971             }, this.dom);
8972             return this;
8973         },
8974         /**
8975          * 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)
8976          * @param {String} className
8977          * @return {Roo.Element} this
8978          */
8979         addClassOnClick : function(className){
8980             var dom = this.dom;
8981             this.on("mousedown", function(){
8982                 Roo.fly(dom, '_internal').addClass(className);
8983                 var d = Roo.get(document);
8984                 var fn = function(){
8985                     Roo.fly(dom, '_internal').removeClass(className);
8986                     d.removeListener("mouseup", fn);
8987                 };
8988                 d.on("mouseup", fn);
8989             });
8990             return this;
8991         },
8992
8993         /**
8994          * Stops the specified event from bubbling and optionally prevents the default action
8995          * @param {String} eventName
8996          * @param {Boolean} preventDefault (optional) true to prevent the default action too
8997          * @return {Roo.Element} this
8998          */
8999         swallowEvent : function(eventName, preventDefault){
9000             var fn = function(e){
9001                 e.stopPropagation();
9002                 if(preventDefault){
9003                     e.preventDefault();
9004                 }
9005             };
9006             if(eventName instanceof Array){
9007                 for(var i = 0, len = eventName.length; i < len; i++){
9008                      this.on(eventName[i], fn);
9009                 }
9010                 return this;
9011             }
9012             this.on(eventName, fn);
9013             return this;
9014         },
9015
9016         /**
9017          * @private
9018          */
9019       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9020
9021         /**
9022          * Sizes this element to its parent element's dimensions performing
9023          * neccessary box adjustments.
9024          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9025          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9026          * @return {Roo.Element} this
9027          */
9028         fitToParent : function(monitorResize, targetParent) {
9029           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9030           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9031           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9032             return;
9033           }
9034           var p = Roo.get(targetParent || this.dom.parentNode);
9035           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9036           if (monitorResize === true) {
9037             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9038             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9039           }
9040           return this;
9041         },
9042
9043         /**
9044          * Gets the next sibling, skipping text nodes
9045          * @return {HTMLElement} The next sibling or null
9046          */
9047         getNextSibling : function(){
9048             var n = this.dom.nextSibling;
9049             while(n && n.nodeType != 1){
9050                 n = n.nextSibling;
9051             }
9052             return n;
9053         },
9054
9055         /**
9056          * Gets the previous sibling, skipping text nodes
9057          * @return {HTMLElement} The previous sibling or null
9058          */
9059         getPrevSibling : function(){
9060             var n = this.dom.previousSibling;
9061             while(n && n.nodeType != 1){
9062                 n = n.previousSibling;
9063             }
9064             return n;
9065         },
9066
9067
9068         /**
9069          * Appends the passed element(s) to this element
9070          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9071          * @return {Roo.Element} this
9072          */
9073         appendChild: function(el){
9074             el = Roo.get(el);
9075             el.appendTo(this);
9076             return this;
9077         },
9078
9079         /**
9080          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9081          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9082          * automatically generated with the specified attributes.
9083          * @param {HTMLElement} insertBefore (optional) a child element of this element
9084          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9085          * @return {Roo.Element} The new child element
9086          */
9087         createChild: function(config, insertBefore, returnDom){
9088             config = config || {tag:'div'};
9089             if(insertBefore){
9090                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9091             }
9092             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9093         },
9094
9095         /**
9096          * Appends this element to the passed element
9097          * @param {String/HTMLElement/Element} el The new parent element
9098          * @return {Roo.Element} this
9099          */
9100         appendTo: function(el){
9101             el = Roo.getDom(el);
9102             el.appendChild(this.dom);
9103             return this;
9104         },
9105
9106         /**
9107          * Inserts this element before the passed element in the DOM
9108          * @param {String/HTMLElement/Element} el The element to insert before
9109          * @return {Roo.Element} this
9110          */
9111         insertBefore: function(el){
9112             el = Roo.getDom(el);
9113             el.parentNode.insertBefore(this.dom, el);
9114             return this;
9115         },
9116
9117         /**
9118          * Inserts this element after the passed element in the DOM
9119          * @param {String/HTMLElement/Element} el The element to insert after
9120          * @return {Roo.Element} this
9121          */
9122         insertAfter: function(el){
9123             el = Roo.getDom(el);
9124             el.parentNode.insertBefore(this.dom, el.nextSibling);
9125             return this;
9126         },
9127
9128         /**
9129          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9130          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9131          * @return {Roo.Element} The new child
9132          */
9133         insertFirst: function(el, returnDom){
9134             el = el || {};
9135             if(typeof el == 'object' && !el.nodeType){ // dh config
9136                 return this.createChild(el, this.dom.firstChild, returnDom);
9137             }else{
9138                 el = Roo.getDom(el);
9139                 this.dom.insertBefore(el, this.dom.firstChild);
9140                 return !returnDom ? Roo.get(el) : el;
9141             }
9142         },
9143
9144         /**
9145          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9146          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9147          * @param {String} where (optional) 'before' or 'after' defaults to before
9148          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9149          * @return {Roo.Element} the inserted Element
9150          */
9151         insertSibling: function(el, where, returnDom){
9152             where = where ? where.toLowerCase() : 'before';
9153             el = el || {};
9154             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9155
9156             if(typeof el == 'object' && !el.nodeType){ // dh config
9157                 if(where == 'after' && !this.dom.nextSibling){
9158                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9159                 }else{
9160                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9161                 }
9162
9163             }else{
9164                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9165                             where == 'before' ? this.dom : this.dom.nextSibling);
9166                 if(!returnDom){
9167                     rt = Roo.get(rt);
9168                 }
9169             }
9170             return rt;
9171         },
9172
9173         /**
9174          * Creates and wraps this element with another element
9175          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9176          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9177          * @return {HTMLElement/Element} The newly created wrapper element
9178          */
9179         wrap: function(config, returnDom){
9180             if(!config){
9181                 config = {tag: "div"};
9182             }
9183             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9184             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9185             return newEl;
9186         },
9187
9188         /**
9189          * Replaces the passed element with this element
9190          * @param {String/HTMLElement/Element} el The element to replace
9191          * @return {Roo.Element} this
9192          */
9193         replace: function(el){
9194             el = Roo.get(el);
9195             this.insertBefore(el);
9196             el.remove();
9197             return this;
9198         },
9199
9200         /**
9201          * Inserts an html fragment into this element
9202          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9203          * @param {String} html The HTML fragment
9204          * @param {Boolean} returnEl True to return an Roo.Element
9205          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9206          */
9207         insertHtml : function(where, html, returnEl){
9208             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9209             return returnEl ? Roo.get(el) : el;
9210         },
9211
9212         /**
9213          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9214          * @param {Object} o The object with the attributes
9215          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9216          * @return {Roo.Element} this
9217          */
9218         set : function(o, useSet){
9219             var el = this.dom;
9220             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9221             for(var attr in o){
9222                 if(attr == "style" || typeof o[attr] == "function") continue;
9223                 if(attr=="cls"){
9224                     el.className = o["cls"];
9225                 }else{
9226                     if(useSet) el.setAttribute(attr, o[attr]);
9227                     else el[attr] = o[attr];
9228                 }
9229             }
9230             if(o.style){
9231                 Roo.DomHelper.applyStyles(el, o.style);
9232             }
9233             return this;
9234         },
9235
9236         /**
9237          * Convenience method for constructing a KeyMap
9238          * @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:
9239          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9240          * @param {Function} fn The function to call
9241          * @param {Object} scope (optional) The scope of the function
9242          * @return {Roo.KeyMap} The KeyMap created
9243          */
9244         addKeyListener : function(key, fn, scope){
9245             var config;
9246             if(typeof key != "object" || key instanceof Array){
9247                 config = {
9248                     key: key,
9249                     fn: fn,
9250                     scope: scope
9251                 };
9252             }else{
9253                 config = {
9254                     key : key.key,
9255                     shift : key.shift,
9256                     ctrl : key.ctrl,
9257                     alt : key.alt,
9258                     fn: fn,
9259                     scope: scope
9260                 };
9261             }
9262             return new Roo.KeyMap(this, config);
9263         },
9264
9265         /**
9266          * Creates a KeyMap for this element
9267          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9268          * @return {Roo.KeyMap} The KeyMap created
9269          */
9270         addKeyMap : function(config){
9271             return new Roo.KeyMap(this, config);
9272         },
9273
9274         /**
9275          * Returns true if this element is scrollable.
9276          * @return {Boolean}
9277          */
9278          isScrollable : function(){
9279             var dom = this.dom;
9280             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9281         },
9282
9283         /**
9284          * 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().
9285          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9286          * @param {Number} value The new scroll value
9287          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9288          * @return {Element} this
9289          */
9290
9291         scrollTo : function(side, value, animate){
9292             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9293             if(!animate || !A){
9294                 this.dom[prop] = value;
9295             }else{
9296                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9297                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9298             }
9299             return this;
9300         },
9301
9302         /**
9303          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9304          * within this element's scrollable range.
9305          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9306          * @param {Number} distance How far to scroll the element in pixels
9307          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9308          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9309          * was scrolled as far as it could go.
9310          */
9311          scroll : function(direction, distance, animate){
9312              if(!this.isScrollable()){
9313                  return;
9314              }
9315              var el = this.dom;
9316              var l = el.scrollLeft, t = el.scrollTop;
9317              var w = el.scrollWidth, h = el.scrollHeight;
9318              var cw = el.clientWidth, ch = el.clientHeight;
9319              direction = direction.toLowerCase();
9320              var scrolled = false;
9321              var a = this.preanim(arguments, 2);
9322              switch(direction){
9323                  case "l":
9324                  case "left":
9325                      if(w - l > cw){
9326                          var v = Math.min(l + distance, w-cw);
9327                          this.scrollTo("left", v, a);
9328                          scrolled = true;
9329                      }
9330                      break;
9331                 case "r":
9332                 case "right":
9333                      if(l > 0){
9334                          var v = Math.max(l - distance, 0);
9335                          this.scrollTo("left", v, a);
9336                          scrolled = true;
9337                      }
9338                      break;
9339                 case "t":
9340                 case "top":
9341                 case "up":
9342                      if(t > 0){
9343                          var v = Math.max(t - distance, 0);
9344                          this.scrollTo("top", v, a);
9345                          scrolled = true;
9346                      }
9347                      break;
9348                 case "b":
9349                 case "bottom":
9350                 case "down":
9351                      if(h - t > ch){
9352                          var v = Math.min(t + distance, h-ch);
9353                          this.scrollTo("top", v, a);
9354                          scrolled = true;
9355                      }
9356                      break;
9357              }
9358              return scrolled;
9359         },
9360
9361         /**
9362          * Translates the passed page coordinates into left/top css values for this element
9363          * @param {Number/Array} x The page x or an array containing [x, y]
9364          * @param {Number} y The page y
9365          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9366          */
9367         translatePoints : function(x, y){
9368             if(typeof x == 'object' || x instanceof Array){
9369                 y = x[1]; x = x[0];
9370             }
9371             var p = this.getStyle('position');
9372             var o = this.getXY();
9373
9374             var l = parseInt(this.getStyle('left'), 10);
9375             var t = parseInt(this.getStyle('top'), 10);
9376
9377             if(isNaN(l)){
9378                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9379             }
9380             if(isNaN(t)){
9381                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9382             }
9383
9384             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9385         },
9386
9387         /**
9388          * Returns the current scroll position of the element.
9389          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9390          */
9391         getScroll : function(){
9392             var d = this.dom, doc = document;
9393             if(d == doc || d == doc.body){
9394                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9395                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9396                 return {left: l, top: t};
9397             }else{
9398                 return {left: d.scrollLeft, top: d.scrollTop};
9399             }
9400         },
9401
9402         /**
9403          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9404          * are convert to standard 6 digit hex color.
9405          * @param {String} attr The css attribute
9406          * @param {String} defaultValue The default value to use when a valid color isn't found
9407          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9408          * YUI color anims.
9409          */
9410         getColor : function(attr, defaultValue, prefix){
9411             var v = this.getStyle(attr);
9412             if(!v || v == "transparent" || v == "inherit") {
9413                 return defaultValue;
9414             }
9415             var color = typeof prefix == "undefined" ? "#" : prefix;
9416             if(v.substr(0, 4) == "rgb("){
9417                 var rvs = v.slice(4, v.length -1).split(",");
9418                 for(var i = 0; i < 3; i++){
9419                     var h = parseInt(rvs[i]).toString(16);
9420                     if(h < 16){
9421                         h = "0" + h;
9422                     }
9423                     color += h;
9424                 }
9425             } else {
9426                 if(v.substr(0, 1) == "#"){
9427                     if(v.length == 4) {
9428                         for(var i = 1; i < 4; i++){
9429                             var c = v.charAt(i);
9430                             color +=  c + c;
9431                         }
9432                     }else if(v.length == 7){
9433                         color += v.substr(1);
9434                     }
9435                 }
9436             }
9437             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9438         },
9439
9440         /**
9441          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9442          * gradient background, rounded corners and a 4-way shadow.
9443          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9444          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9445          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9446          * @return {Roo.Element} this
9447          */
9448         boxWrap : function(cls){
9449             cls = cls || 'x-box';
9450             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9451             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9452             return el;
9453         },
9454
9455         /**
9456          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9457          * @param {String} namespace The namespace in which to look for the attribute
9458          * @param {String} name The attribute name
9459          * @return {String} The attribute value
9460          */
9461         getAttributeNS : Roo.isIE ? function(ns, name){
9462             var d = this.dom;
9463             var type = typeof d[ns+":"+name];
9464             if(type != 'undefined' && type != 'unknown'){
9465                 return d[ns+":"+name];
9466             }
9467             return d[name];
9468         } : function(ns, name){
9469             var d = this.dom;
9470             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9471         }
9472     };
9473
9474     var ep = El.prototype;
9475
9476     /**
9477      * Appends an event handler (Shorthand for addListener)
9478      * @param {String}   eventName     The type of event to append
9479      * @param {Function} fn        The method the event invokes
9480      * @param {Object} scope       (optional) The scope (this object) of the fn
9481      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9482      * @method
9483      */
9484     ep.on = ep.addListener;
9485         // backwards compat
9486     ep.mon = ep.addListener;
9487
9488     /**
9489      * Removes an event handler from this element (shorthand for removeListener)
9490      * @param {String} eventName the type of event to remove
9491      * @param {Function} fn the method the event invokes
9492      * @return {Roo.Element} this
9493      * @method
9494      */
9495     ep.un = ep.removeListener;
9496
9497     /**
9498      * true to automatically adjust width and height settings for box-model issues (default to true)
9499      */
9500     ep.autoBoxAdjust = true;
9501
9502     // private
9503     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9504
9505     // private
9506     El.addUnits = function(v, defaultUnit){
9507         if(v === "" || v == "auto"){
9508             return v;
9509         }
9510         if(v === undefined){
9511             return '';
9512         }
9513         if(typeof v == "number" || !El.unitPattern.test(v)){
9514             return v + (defaultUnit || 'px');
9515         }
9516         return v;
9517     };
9518
9519     // special markup used throughout Roo when box wrapping elements
9520     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>';
9521     /**
9522      * Visibility mode constant - Use visibility to hide element
9523      * @static
9524      * @type Number
9525      */
9526     El.VISIBILITY = 1;
9527     /**
9528      * Visibility mode constant - Use display to hide element
9529      * @static
9530      * @type Number
9531      */
9532     El.DISPLAY = 2;
9533
9534     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9535     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9536     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9537
9538
9539
9540     /**
9541      * @private
9542      */
9543     El.cache = {};
9544
9545     var docEl;
9546
9547     /**
9548      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9549      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9550      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9551      * @return {Element} The Element object
9552      * @static
9553      */
9554     El.get = function(el){
9555         var ex, elm, id;
9556         if(!el){ return null; }
9557         if(typeof el == "string"){ // element id
9558             if(!(elm = document.getElementById(el))){
9559                 return null;
9560             }
9561             if(ex = El.cache[el]){
9562                 ex.dom = elm;
9563             }else{
9564                 ex = El.cache[el] = new El(elm);
9565             }
9566             return ex;
9567         }else if(el.tagName){ // dom element
9568             if(!(id = el.id)){
9569                 id = Roo.id(el);
9570             }
9571             if(ex = El.cache[id]){
9572                 ex.dom = el;
9573             }else{
9574                 ex = El.cache[id] = new El(el);
9575             }
9576             return ex;
9577         }else if(el instanceof El){
9578             if(el != docEl){
9579                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9580                                                               // catch case where it hasn't been appended
9581                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9582             }
9583             return el;
9584         }else if(el.isComposite){
9585             return el;
9586         }else if(el instanceof Array){
9587             return El.select(el);
9588         }else if(el == document){
9589             // create a bogus element object representing the document object
9590             if(!docEl){
9591                 var f = function(){};
9592                 f.prototype = El.prototype;
9593                 docEl = new f();
9594                 docEl.dom = document;
9595             }
9596             return docEl;
9597         }
9598         return null;
9599     };
9600
9601     // private
9602     El.uncache = function(el){
9603         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9604             if(a[i]){
9605                 delete El.cache[a[i].id || a[i]];
9606             }
9607         }
9608     };
9609
9610     // private
9611     // Garbage collection - uncache elements/purge listeners on orphaned elements
9612     // so we don't hold a reference and cause the browser to retain them
9613     El.garbageCollect = function(){
9614         if(!Roo.enableGarbageCollector){
9615             clearInterval(El.collectorThread);
9616             return;
9617         }
9618         for(var eid in El.cache){
9619             var el = El.cache[eid], d = el.dom;
9620             // -------------------------------------------------------
9621             // Determining what is garbage:
9622             // -------------------------------------------------------
9623             // !d
9624             // dom node is null, definitely garbage
9625             // -------------------------------------------------------
9626             // !d.parentNode
9627             // no parentNode == direct orphan, definitely garbage
9628             // -------------------------------------------------------
9629             // !d.offsetParent && !document.getElementById(eid)
9630             // display none elements have no offsetParent so we will
9631             // also try to look it up by it's id. However, check
9632             // offsetParent first so we don't do unneeded lookups.
9633             // This enables collection of elements that are not orphans
9634             // directly, but somewhere up the line they have an orphan
9635             // parent.
9636             // -------------------------------------------------------
9637             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9638                 delete El.cache[eid];
9639                 if(d && Roo.enableListenerCollection){
9640                     E.purgeElement(d);
9641                 }
9642             }
9643         }
9644     }
9645     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9646
9647
9648     // dom is optional
9649     El.Flyweight = function(dom){
9650         this.dom = dom;
9651     };
9652     El.Flyweight.prototype = El.prototype;
9653
9654     El._flyweights = {};
9655     /**
9656      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9657      * the dom node can be overwritten by other code.
9658      * @param {String/HTMLElement} el The dom node or id
9659      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9660      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9661      * @static
9662      * @return {Element} The shared Element object
9663      */
9664     El.fly = function(el, named){
9665         named = named || '_global';
9666         el = Roo.getDom(el);
9667         if(!el){
9668             return null;
9669         }
9670         if(!El._flyweights[named]){
9671             El._flyweights[named] = new El.Flyweight();
9672         }
9673         El._flyweights[named].dom = el;
9674         return El._flyweights[named];
9675     };
9676
9677     /**
9678      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9679      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9680      * Shorthand of {@link Roo.Element#get}
9681      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9682      * @return {Element} The Element object
9683      * @member Roo
9684      * @method get
9685      */
9686     Roo.get = El.get;
9687     /**
9688      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9689      * the dom node can be overwritten by other code.
9690      * Shorthand of {@link Roo.Element#fly}
9691      * @param {String/HTMLElement} el The dom node or id
9692      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9693      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9694      * @static
9695      * @return {Element} The shared Element object
9696      * @member Roo
9697      * @method fly
9698      */
9699     Roo.fly = El.fly;
9700
9701     // speedy lookup for elements never to box adjust
9702     var noBoxAdjust = Roo.isStrict ? {
9703         select:1
9704     } : {
9705         input:1, select:1, textarea:1
9706     };
9707     if(Roo.isIE || Roo.isGecko){
9708         noBoxAdjust['button'] = 1;
9709     }
9710
9711
9712     Roo.EventManager.on(window, 'unload', function(){
9713         delete El.cache;
9714         delete El._flyweights;
9715     });
9716 })();
9717
9718
9719
9720
9721 if(Roo.DomQuery){
9722     Roo.Element.selectorFunction = Roo.DomQuery.select;
9723 }
9724
9725 Roo.Element.select = function(selector, unique, root){
9726     var els;
9727     if(typeof selector == "string"){
9728         els = Roo.Element.selectorFunction(selector, root);
9729     }else if(selector.length !== undefined){
9730         els = selector;
9731     }else{
9732         throw "Invalid selector";
9733     }
9734     if(unique === true){
9735         return new Roo.CompositeElement(els);
9736     }else{
9737         return new Roo.CompositeElementLite(els);
9738     }
9739 };
9740 /**
9741  * Selects elements based on the passed CSS selector to enable working on them as 1.
9742  * @param {String/Array} selector The CSS selector or an array of elements
9743  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
9744  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9745  * @return {CompositeElementLite/CompositeElement}
9746  * @member Roo
9747  * @method select
9748  */
9749 Roo.select = Roo.Element.select;
9750
9751
9752
9753
9754
9755
9756
9757
9758
9759
9760
9761
9762
9763
9764 /*
9765  * Based on:
9766  * Ext JS Library 1.1.1
9767  * Copyright(c) 2006-2007, Ext JS, LLC.
9768  *
9769  * Originally Released Under LGPL - original licence link has changed is not relivant.
9770  *
9771  * Fork - LGPL
9772  * <script type="text/javascript">
9773  */
9774
9775
9776
9777 //Notifies Element that fx methods are available
9778 Roo.enableFx = true;
9779
9780 /**
9781  * @class Roo.Fx
9782  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
9783  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
9784  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
9785  * Element effects to work.</p><br/>
9786  *
9787  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
9788  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
9789  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
9790  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
9791  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
9792  * expected results and should be done with care.</p><br/>
9793  *
9794  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
9795  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
9796 <pre>
9797 Value  Description
9798 -----  -----------------------------
9799 tl     The top left corner
9800 t      The center of the top edge
9801 tr     The top right corner
9802 l      The center of the left edge
9803 r      The center of the right edge
9804 bl     The bottom left corner
9805 b      The center of the bottom edge
9806 br     The bottom right corner
9807 </pre>
9808  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
9809  * below are common options that can be passed to any Fx method.</b>
9810  * @cfg {Function} callback A function called when the effect is finished
9811  * @cfg {Object} scope The scope of the effect function
9812  * @cfg {String} easing A valid Easing value for the effect
9813  * @cfg {String} afterCls A css class to apply after the effect
9814  * @cfg {Number} duration The length of time (in seconds) that the effect should last
9815  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
9816  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
9817  * effects that end with the element being visually hidden, ignored otherwise)
9818  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
9819  * a function which returns such a specification that will be applied to the Element after the effect finishes
9820  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
9821  * @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
9822  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
9823  */
9824 Roo.Fx = {
9825         /**
9826          * Slides the element into view.  An anchor point can be optionally passed to set the point of
9827          * origin for the slide effect.  This function automatically handles wrapping the element with
9828          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9829          * Usage:
9830          *<pre><code>
9831 // default: slide the element in from the top
9832 el.slideIn();
9833
9834 // custom: slide the element in from the right with a 2-second duration
9835 el.slideIn('r', { duration: 2 });
9836
9837 // common config options shown with default values
9838 el.slideIn('t', {
9839     easing: 'easeOut',
9840     duration: .5
9841 });
9842 </code></pre>
9843          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9844          * @param {Object} options (optional) Object literal with any of the Fx config options
9845          * @return {Roo.Element} The Element
9846          */
9847     slideIn : function(anchor, o){
9848         var el = this.getFxEl();
9849         o = o || {};
9850
9851         el.queueFx(o, function(){
9852
9853             anchor = anchor || "t";
9854
9855             // fix display to visibility
9856             this.fixDisplay();
9857
9858             // restore values after effect
9859             var r = this.getFxRestore();
9860             var b = this.getBox();
9861             // fixed size for slide
9862             this.setSize(b);
9863
9864             // wrap if needed
9865             var wrap = this.fxWrap(r.pos, o, "hidden");
9866
9867             var st = this.dom.style;
9868             st.visibility = "visible";
9869             st.position = "absolute";
9870
9871             // clear out temp styles after slide and unwrap
9872             var after = function(){
9873                 el.fxUnwrap(wrap, r.pos, o);
9874                 st.width = r.width;
9875                 st.height = r.height;
9876                 el.afterFx(o);
9877             };
9878             // time to calc the positions
9879             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
9880
9881             switch(anchor.toLowerCase()){
9882                 case "t":
9883                     wrap.setSize(b.width, 0);
9884                     st.left = st.bottom = "0";
9885                     a = {height: bh};
9886                 break;
9887                 case "l":
9888                     wrap.setSize(0, b.height);
9889                     st.right = st.top = "0";
9890                     a = {width: bw};
9891                 break;
9892                 case "r":
9893                     wrap.setSize(0, b.height);
9894                     wrap.setX(b.right);
9895                     st.left = st.top = "0";
9896                     a = {width: bw, points: pt};
9897                 break;
9898                 case "b":
9899                     wrap.setSize(b.width, 0);
9900                     wrap.setY(b.bottom);
9901                     st.left = st.top = "0";
9902                     a = {height: bh, points: pt};
9903                 break;
9904                 case "tl":
9905                     wrap.setSize(0, 0);
9906                     st.right = st.bottom = "0";
9907                     a = {width: bw, height: bh};
9908                 break;
9909                 case "bl":
9910                     wrap.setSize(0, 0);
9911                     wrap.setY(b.y+b.height);
9912                     st.right = st.top = "0";
9913                     a = {width: bw, height: bh, points: pt};
9914                 break;
9915                 case "br":
9916                     wrap.setSize(0, 0);
9917                     wrap.setXY([b.right, b.bottom]);
9918                     st.left = st.top = "0";
9919                     a = {width: bw, height: bh, points: pt};
9920                 break;
9921                 case "tr":
9922                     wrap.setSize(0, 0);
9923                     wrap.setX(b.x+b.width);
9924                     st.left = st.bottom = "0";
9925                     a = {width: bw, height: bh, points: pt};
9926                 break;
9927             }
9928             this.dom.style.visibility = "visible";
9929             wrap.show();
9930
9931             arguments.callee.anim = wrap.fxanim(a,
9932                 o,
9933                 'motion',
9934                 .5,
9935                 'easeOut', after);
9936         });
9937         return this;
9938     },
9939     
9940         /**
9941          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
9942          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
9943          * 'hidden') but block elements will still take up space in the document.  The element must be removed
9944          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
9945          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9946          * Usage:
9947          *<pre><code>
9948 // default: slide the element out to the top
9949 el.slideOut();
9950
9951 // custom: slide the element out to the right with a 2-second duration
9952 el.slideOut('r', { duration: 2 });
9953
9954 // common config options shown with default values
9955 el.slideOut('t', {
9956     easing: 'easeOut',
9957     duration: .5,
9958     remove: false,
9959     useDisplay: false
9960 });
9961 </code></pre>
9962          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9963          * @param {Object} options (optional) Object literal with any of the Fx config options
9964          * @return {Roo.Element} The Element
9965          */
9966     slideOut : function(anchor, o){
9967         var el = this.getFxEl();
9968         o = o || {};
9969
9970         el.queueFx(o, function(){
9971
9972             anchor = anchor || "t";
9973
9974             // restore values after effect
9975             var r = this.getFxRestore();
9976             
9977             var b = this.getBox();
9978             // fixed size for slide
9979             this.setSize(b);
9980
9981             // wrap if needed
9982             var wrap = this.fxWrap(r.pos, o, "visible");
9983
9984             var st = this.dom.style;
9985             st.visibility = "visible";
9986             st.position = "absolute";
9987
9988             wrap.setSize(b);
9989
9990             var after = function(){
9991                 if(o.useDisplay){
9992                     el.setDisplayed(false);
9993                 }else{
9994                     el.hide();
9995                 }
9996
9997                 el.fxUnwrap(wrap, r.pos, o);
9998
9999                 st.width = r.width;
10000                 st.height = r.height;
10001
10002                 el.afterFx(o);
10003             };
10004
10005             var a, zero = {to: 0};
10006             switch(anchor.toLowerCase()){
10007                 case "t":
10008                     st.left = st.bottom = "0";
10009                     a = {height: zero};
10010                 break;
10011                 case "l":
10012                     st.right = st.top = "0";
10013                     a = {width: zero};
10014                 break;
10015                 case "r":
10016                     st.left = st.top = "0";
10017                     a = {width: zero, points: {to:[b.right, b.y]}};
10018                 break;
10019                 case "b":
10020                     st.left = st.top = "0";
10021                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10022                 break;
10023                 case "tl":
10024                     st.right = st.bottom = "0";
10025                     a = {width: zero, height: zero};
10026                 break;
10027                 case "bl":
10028                     st.right = st.top = "0";
10029                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10030                 break;
10031                 case "br":
10032                     st.left = st.top = "0";
10033                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10034                 break;
10035                 case "tr":
10036                     st.left = st.bottom = "0";
10037                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10038                 break;
10039             }
10040
10041             arguments.callee.anim = wrap.fxanim(a,
10042                 o,
10043                 'motion',
10044                 .5,
10045                 "easeOut", after);
10046         });
10047         return this;
10048     },
10049
10050         /**
10051          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10052          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10053          * The element must be removed from the DOM using the 'remove' config option if desired.
10054          * Usage:
10055          *<pre><code>
10056 // default
10057 el.puff();
10058
10059 // common config options shown with default values
10060 el.puff({
10061     easing: 'easeOut',
10062     duration: .5,
10063     remove: false,
10064     useDisplay: false
10065 });
10066 </code></pre>
10067          * @param {Object} options (optional) Object literal with any of the Fx config options
10068          * @return {Roo.Element} The Element
10069          */
10070     puff : function(o){
10071         var el = this.getFxEl();
10072         o = o || {};
10073
10074         el.queueFx(o, function(){
10075             this.clearOpacity();
10076             this.show();
10077
10078             // restore values after effect
10079             var r = this.getFxRestore();
10080             var st = this.dom.style;
10081
10082             var after = function(){
10083                 if(o.useDisplay){
10084                     el.setDisplayed(false);
10085                 }else{
10086                     el.hide();
10087                 }
10088
10089                 el.clearOpacity();
10090
10091                 el.setPositioning(r.pos);
10092                 st.width = r.width;
10093                 st.height = r.height;
10094                 st.fontSize = '';
10095                 el.afterFx(o);
10096             };
10097
10098             var width = this.getWidth();
10099             var height = this.getHeight();
10100
10101             arguments.callee.anim = this.fxanim({
10102                     width : {to: this.adjustWidth(width * 2)},
10103                     height : {to: this.adjustHeight(height * 2)},
10104                     points : {by: [-(width * .5), -(height * .5)]},
10105                     opacity : {to: 0},
10106                     fontSize: {to:200, unit: "%"}
10107                 },
10108                 o,
10109                 'motion',
10110                 .5,
10111                 "easeOut", after);
10112         });
10113         return this;
10114     },
10115
10116         /**
10117          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10118          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10119          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10120          * Usage:
10121          *<pre><code>
10122 // default
10123 el.switchOff();
10124
10125 // all config options shown with default values
10126 el.switchOff({
10127     easing: 'easeIn',
10128     duration: .3,
10129     remove: false,
10130     useDisplay: false
10131 });
10132 </code></pre>
10133          * @param {Object} options (optional) Object literal with any of the Fx config options
10134          * @return {Roo.Element} The Element
10135          */
10136     switchOff : function(o){
10137         var el = this.getFxEl();
10138         o = o || {};
10139
10140         el.queueFx(o, function(){
10141             this.clearOpacity();
10142             this.clip();
10143
10144             // restore values after effect
10145             var r = this.getFxRestore();
10146             var st = this.dom.style;
10147
10148             var after = function(){
10149                 if(o.useDisplay){
10150                     el.setDisplayed(false);
10151                 }else{
10152                     el.hide();
10153                 }
10154
10155                 el.clearOpacity();
10156                 el.setPositioning(r.pos);
10157                 st.width = r.width;
10158                 st.height = r.height;
10159
10160                 el.afterFx(o);
10161             };
10162
10163             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10164                 this.clearOpacity();
10165                 (function(){
10166                     this.fxanim({
10167                         height:{to:1},
10168                         points:{by:[0, this.getHeight() * .5]}
10169                     }, o, 'motion', 0.3, 'easeIn', after);
10170                 }).defer(100, this);
10171             });
10172         });
10173         return this;
10174     },
10175
10176     /**
10177      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10178      * changed using the "attr" config option) and then fading back to the original color. If no original
10179      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10180      * Usage:
10181 <pre><code>
10182 // default: highlight background to yellow
10183 el.highlight();
10184
10185 // custom: highlight foreground text to blue for 2 seconds
10186 el.highlight("0000ff", { attr: 'color', duration: 2 });
10187
10188 // common config options shown with default values
10189 el.highlight("ffff9c", {
10190     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10191     endColor: (current color) or "ffffff",
10192     easing: 'easeIn',
10193     duration: 1
10194 });
10195 </code></pre>
10196      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10197      * @param {Object} options (optional) Object literal with any of the Fx config options
10198      * @return {Roo.Element} The Element
10199      */ 
10200     highlight : function(color, o){
10201         var el = this.getFxEl();
10202         o = o || {};
10203
10204         el.queueFx(o, function(){
10205             color = color || "ffff9c";
10206             attr = o.attr || "backgroundColor";
10207
10208             this.clearOpacity();
10209             this.show();
10210
10211             var origColor = this.getColor(attr);
10212             var restoreColor = this.dom.style[attr];
10213             endColor = (o.endColor || origColor) || "ffffff";
10214
10215             var after = function(){
10216                 el.dom.style[attr] = restoreColor;
10217                 el.afterFx(o);
10218             };
10219
10220             var a = {};
10221             a[attr] = {from: color, to: endColor};
10222             arguments.callee.anim = this.fxanim(a,
10223                 o,
10224                 'color',
10225                 1,
10226                 'easeIn', after);
10227         });
10228         return this;
10229     },
10230
10231    /**
10232     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10233     * Usage:
10234 <pre><code>
10235 // default: a single light blue ripple
10236 el.frame();
10237
10238 // custom: 3 red ripples lasting 3 seconds total
10239 el.frame("ff0000", 3, { duration: 3 });
10240
10241 // common config options shown with default values
10242 el.frame("C3DAF9", 1, {
10243     duration: 1 //duration of entire animation (not each individual ripple)
10244     // Note: Easing is not configurable and will be ignored if included
10245 });
10246 </code></pre>
10247     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10248     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10249     * @param {Object} options (optional) Object literal with any of the Fx config options
10250     * @return {Roo.Element} The Element
10251     */
10252     frame : function(color, count, o){
10253         var el = this.getFxEl();
10254         o = o || {};
10255
10256         el.queueFx(o, function(){
10257             color = color || "#C3DAF9";
10258             if(color.length == 6){
10259                 color = "#" + color;
10260             }
10261             count = count || 1;
10262             duration = o.duration || 1;
10263             this.show();
10264
10265             var b = this.getBox();
10266             var animFn = function(){
10267                 var proxy = this.createProxy({
10268
10269                      style:{
10270                         visbility:"hidden",
10271                         position:"absolute",
10272                         "z-index":"35000", // yee haw
10273                         border:"0px solid " + color
10274                      }
10275                   });
10276                 var scale = Roo.isBorderBox ? 2 : 1;
10277                 proxy.animate({
10278                     top:{from:b.y, to:b.y - 20},
10279                     left:{from:b.x, to:b.x - 20},
10280                     borderWidth:{from:0, to:10},
10281                     opacity:{from:1, to:0},
10282                     height:{from:b.height, to:(b.height + (20*scale))},
10283                     width:{from:b.width, to:(b.width + (20*scale))}
10284                 }, duration, function(){
10285                     proxy.remove();
10286                 });
10287                 if(--count > 0){
10288                      animFn.defer((duration/2)*1000, this);
10289                 }else{
10290                     el.afterFx(o);
10291                 }
10292             };
10293             animFn.call(this);
10294         });
10295         return this;
10296     },
10297
10298    /**
10299     * Creates a pause before any subsequent queued effects begin.  If there are
10300     * no effects queued after the pause it will have no effect.
10301     * Usage:
10302 <pre><code>
10303 el.pause(1);
10304 </code></pre>
10305     * @param {Number} seconds The length of time to pause (in seconds)
10306     * @return {Roo.Element} The Element
10307     */
10308     pause : function(seconds){
10309         var el = this.getFxEl();
10310         var o = {};
10311
10312         el.queueFx(o, function(){
10313             setTimeout(function(){
10314                 el.afterFx(o);
10315             }, seconds * 1000);
10316         });
10317         return this;
10318     },
10319
10320    /**
10321     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10322     * using the "endOpacity" config option.
10323     * Usage:
10324 <pre><code>
10325 // default: fade in from opacity 0 to 100%
10326 el.fadeIn();
10327
10328 // custom: fade in from opacity 0 to 75% over 2 seconds
10329 el.fadeIn({ endOpacity: .75, duration: 2});
10330
10331 // common config options shown with default values
10332 el.fadeIn({
10333     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10334     easing: 'easeOut',
10335     duration: .5
10336 });
10337 </code></pre>
10338     * @param {Object} options (optional) Object literal with any of the Fx config options
10339     * @return {Roo.Element} The Element
10340     */
10341     fadeIn : function(o){
10342         var el = this.getFxEl();
10343         o = o || {};
10344         el.queueFx(o, function(){
10345             this.setOpacity(0);
10346             this.fixDisplay();
10347             this.dom.style.visibility = 'visible';
10348             var to = o.endOpacity || 1;
10349             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10350                 o, null, .5, "easeOut", function(){
10351                 if(to == 1){
10352                     this.clearOpacity();
10353                 }
10354                 el.afterFx(o);
10355             });
10356         });
10357         return this;
10358     },
10359
10360    /**
10361     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10362     * using the "endOpacity" config option.
10363     * Usage:
10364 <pre><code>
10365 // default: fade out from the element's current opacity to 0
10366 el.fadeOut();
10367
10368 // custom: fade out from the element's current opacity to 25% over 2 seconds
10369 el.fadeOut({ endOpacity: .25, duration: 2});
10370
10371 // common config options shown with default values
10372 el.fadeOut({
10373     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10374     easing: 'easeOut',
10375     duration: .5
10376     remove: false,
10377     useDisplay: false
10378 });
10379 </code></pre>
10380     * @param {Object} options (optional) Object literal with any of the Fx config options
10381     * @return {Roo.Element} The Element
10382     */
10383     fadeOut : function(o){
10384         var el = this.getFxEl();
10385         o = o || {};
10386         el.queueFx(o, function(){
10387             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10388                 o, null, .5, "easeOut", function(){
10389                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10390                      this.dom.style.display = "none";
10391                 }else{
10392                      this.dom.style.visibility = "hidden";
10393                 }
10394                 this.clearOpacity();
10395                 el.afterFx(o);
10396             });
10397         });
10398         return this;
10399     },
10400
10401    /**
10402     * Animates the transition of an element's dimensions from a starting height/width
10403     * to an ending height/width.
10404     * Usage:
10405 <pre><code>
10406 // change height and width to 100x100 pixels
10407 el.scale(100, 100);
10408
10409 // common config options shown with default values.  The height and width will default to
10410 // the element's existing values if passed as null.
10411 el.scale(
10412     [element's width],
10413     [element's height], {
10414     easing: 'easeOut',
10415     duration: .35
10416 });
10417 </code></pre>
10418     * @param {Number} width  The new width (pass undefined to keep the original width)
10419     * @param {Number} height  The new height (pass undefined to keep the original height)
10420     * @param {Object} options (optional) Object literal with any of the Fx config options
10421     * @return {Roo.Element} The Element
10422     */
10423     scale : function(w, h, o){
10424         this.shift(Roo.apply({}, o, {
10425             width: w,
10426             height: h
10427         }));
10428         return this;
10429     },
10430
10431    /**
10432     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10433     * Any of these properties not specified in the config object will not be changed.  This effect 
10434     * requires that at least one new dimension, position or opacity setting must be passed in on
10435     * the config object in order for the function to have any effect.
10436     * Usage:
10437 <pre><code>
10438 // slide the element horizontally to x position 200 while changing the height and opacity
10439 el.shift({ x: 200, height: 50, opacity: .8 });
10440
10441 // common config options shown with default values.
10442 el.shift({
10443     width: [element's width],
10444     height: [element's height],
10445     x: [element's x position],
10446     y: [element's y position],
10447     opacity: [element's opacity],
10448     easing: 'easeOut',
10449     duration: .35
10450 });
10451 </code></pre>
10452     * @param {Object} options  Object literal with any of the Fx config options
10453     * @return {Roo.Element} The Element
10454     */
10455     shift : function(o){
10456         var el = this.getFxEl();
10457         o = o || {};
10458         el.queueFx(o, function(){
10459             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10460             if(w !== undefined){
10461                 a.width = {to: this.adjustWidth(w)};
10462             }
10463             if(h !== undefined){
10464                 a.height = {to: this.adjustHeight(h)};
10465             }
10466             if(x !== undefined || y !== undefined){
10467                 a.points = {to: [
10468                     x !== undefined ? x : this.getX(),
10469                     y !== undefined ? y : this.getY()
10470                 ]};
10471             }
10472             if(op !== undefined){
10473                 a.opacity = {to: op};
10474             }
10475             if(o.xy !== undefined){
10476                 a.points = {to: o.xy};
10477             }
10478             arguments.callee.anim = this.fxanim(a,
10479                 o, 'motion', .35, "easeOut", function(){
10480                 el.afterFx(o);
10481             });
10482         });
10483         return this;
10484     },
10485
10486         /**
10487          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10488          * ending point of the effect.
10489          * Usage:
10490          *<pre><code>
10491 // default: slide the element downward while fading out
10492 el.ghost();
10493
10494 // custom: slide the element out to the right with a 2-second duration
10495 el.ghost('r', { duration: 2 });
10496
10497 // common config options shown with default values
10498 el.ghost('b', {
10499     easing: 'easeOut',
10500     duration: .5
10501     remove: false,
10502     useDisplay: false
10503 });
10504 </code></pre>
10505          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10506          * @param {Object} options (optional) Object literal with any of the Fx config options
10507          * @return {Roo.Element} The Element
10508          */
10509     ghost : function(anchor, o){
10510         var el = this.getFxEl();
10511         o = o || {};
10512
10513         el.queueFx(o, function(){
10514             anchor = anchor || "b";
10515
10516             // restore values after effect
10517             var r = this.getFxRestore();
10518             var w = this.getWidth(),
10519                 h = this.getHeight();
10520
10521             var st = this.dom.style;
10522
10523             var after = function(){
10524                 if(o.useDisplay){
10525                     el.setDisplayed(false);
10526                 }else{
10527                     el.hide();
10528                 }
10529
10530                 el.clearOpacity();
10531                 el.setPositioning(r.pos);
10532                 st.width = r.width;
10533                 st.height = r.height;
10534
10535                 el.afterFx(o);
10536             };
10537
10538             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10539             switch(anchor.toLowerCase()){
10540                 case "t":
10541                     pt.by = [0, -h];
10542                 break;
10543                 case "l":
10544                     pt.by = [-w, 0];
10545                 break;
10546                 case "r":
10547                     pt.by = [w, 0];
10548                 break;
10549                 case "b":
10550                     pt.by = [0, h];
10551                 break;
10552                 case "tl":
10553                     pt.by = [-w, -h];
10554                 break;
10555                 case "bl":
10556                     pt.by = [-w, h];
10557                 break;
10558                 case "br":
10559                     pt.by = [w, h];
10560                 break;
10561                 case "tr":
10562                     pt.by = [w, -h];
10563                 break;
10564             }
10565
10566             arguments.callee.anim = this.fxanim(a,
10567                 o,
10568                 'motion',
10569                 .5,
10570                 "easeOut", after);
10571         });
10572         return this;
10573     },
10574
10575         /**
10576          * Ensures that all effects queued after syncFx is called on the element are
10577          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10578          * @return {Roo.Element} The Element
10579          */
10580     syncFx : function(){
10581         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10582             block : false,
10583             concurrent : true,
10584             stopFx : false
10585         });
10586         return this;
10587     },
10588
10589         /**
10590          * Ensures that all effects queued after sequenceFx is called on the element are
10591          * run in sequence.  This is the opposite of {@link #syncFx}.
10592          * @return {Roo.Element} The Element
10593          */
10594     sequenceFx : function(){
10595         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10596             block : false,
10597             concurrent : false,
10598             stopFx : false
10599         });
10600         return this;
10601     },
10602
10603         /* @private */
10604     nextFx : function(){
10605         var ef = this.fxQueue[0];
10606         if(ef){
10607             ef.call(this);
10608         }
10609     },
10610
10611         /**
10612          * Returns true if the element has any effects actively running or queued, else returns false.
10613          * @return {Boolean} True if element has active effects, else false
10614          */
10615     hasActiveFx : function(){
10616         return this.fxQueue && this.fxQueue[0];
10617     },
10618
10619         /**
10620          * Stops any running effects and clears the element's internal effects queue if it contains
10621          * any additional effects that haven't started yet.
10622          * @return {Roo.Element} The Element
10623          */
10624     stopFx : function(){
10625         if(this.hasActiveFx()){
10626             var cur = this.fxQueue[0];
10627             if(cur && cur.anim && cur.anim.isAnimated()){
10628                 this.fxQueue = [cur]; // clear out others
10629                 cur.anim.stop(true);
10630             }
10631         }
10632         return this;
10633     },
10634
10635         /* @private */
10636     beforeFx : function(o){
10637         if(this.hasActiveFx() && !o.concurrent){
10638            if(o.stopFx){
10639                this.stopFx();
10640                return true;
10641            }
10642            return false;
10643         }
10644         return true;
10645     },
10646
10647         /**
10648          * Returns true if the element is currently blocking so that no other effect can be queued
10649          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10650          * used to ensure that an effect initiated by a user action runs to completion prior to the
10651          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10652          * @return {Boolean} True if blocking, else false
10653          */
10654     hasFxBlock : function(){
10655         var q = this.fxQueue;
10656         return q && q[0] && q[0].block;
10657     },
10658
10659         /* @private */
10660     queueFx : function(o, fn){
10661         if(!this.fxQueue){
10662             this.fxQueue = [];
10663         }
10664         if(!this.hasFxBlock()){
10665             Roo.applyIf(o, this.fxDefaults);
10666             if(!o.concurrent){
10667                 var run = this.beforeFx(o);
10668                 fn.block = o.block;
10669                 this.fxQueue.push(fn);
10670                 if(run){
10671                     this.nextFx();
10672                 }
10673             }else{
10674                 fn.call(this);
10675             }
10676         }
10677         return this;
10678     },
10679
10680         /* @private */
10681     fxWrap : function(pos, o, vis){
10682         var wrap;
10683         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10684             var wrapXY;
10685             if(o.fixPosition){
10686                 wrapXY = this.getXY();
10687             }
10688             var div = document.createElement("div");
10689             div.style.visibility = vis;
10690             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10691             wrap.setPositioning(pos);
10692             if(wrap.getStyle("position") == "static"){
10693                 wrap.position("relative");
10694             }
10695             this.clearPositioning('auto');
10696             wrap.clip();
10697             wrap.dom.appendChild(this.dom);
10698             if(wrapXY){
10699                 wrap.setXY(wrapXY);
10700             }
10701         }
10702         return wrap;
10703     },
10704
10705         /* @private */
10706     fxUnwrap : function(wrap, pos, o){
10707         this.clearPositioning();
10708         this.setPositioning(pos);
10709         if(!o.wrap){
10710             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
10711             wrap.remove();
10712         }
10713     },
10714
10715         /* @private */
10716     getFxRestore : function(){
10717         var st = this.dom.style;
10718         return {pos: this.getPositioning(), width: st.width, height : st.height};
10719     },
10720
10721         /* @private */
10722     afterFx : function(o){
10723         if(o.afterStyle){
10724             this.applyStyles(o.afterStyle);
10725         }
10726         if(o.afterCls){
10727             this.addClass(o.afterCls);
10728         }
10729         if(o.remove === true){
10730             this.remove();
10731         }
10732         Roo.callback(o.callback, o.scope, [this]);
10733         if(!o.concurrent){
10734             this.fxQueue.shift();
10735             this.nextFx();
10736         }
10737     },
10738
10739         /* @private */
10740     getFxEl : function(){ // support for composite element fx
10741         return Roo.get(this.dom);
10742     },
10743
10744         /* @private */
10745     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
10746         animType = animType || 'run';
10747         opt = opt || {};
10748         var anim = Roo.lib.Anim[animType](
10749             this.dom, args,
10750             (opt.duration || defaultDur) || .35,
10751             (opt.easing || defaultEase) || 'easeOut',
10752             function(){
10753                 Roo.callback(cb, this);
10754             },
10755             this
10756         );
10757         opt.anim = anim;
10758         return anim;
10759     }
10760 };
10761
10762 // backwords compat
10763 Roo.Fx.resize = Roo.Fx.scale;
10764
10765 //When included, Roo.Fx is automatically applied to Element so that all basic
10766 //effects are available directly via the Element API
10767 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
10768  * Based on:
10769  * Ext JS Library 1.1.1
10770  * Copyright(c) 2006-2007, Ext JS, LLC.
10771  *
10772  * Originally Released Under LGPL - original licence link has changed is not relivant.
10773  *
10774  * Fork - LGPL
10775  * <script type="text/javascript">
10776  */
10777
10778
10779 /**
10780  * @class Roo.CompositeElement
10781  * Standard composite class. Creates a Roo.Element for every element in the collection.
10782  * <br><br>
10783  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10784  * actions will be performed on all the elements in this collection.</b>
10785  * <br><br>
10786  * All methods return <i>this</i> and can be chained.
10787  <pre><code>
10788  var els = Roo.select("#some-el div.some-class", true);
10789  // or select directly from an existing element
10790  var el = Roo.get('some-el');
10791  el.select('div.some-class', true);
10792
10793  els.setWidth(100); // all elements become 100 width
10794  els.hide(true); // all elements fade out and hide
10795  // or
10796  els.setWidth(100).hide(true);
10797  </code></pre>
10798  */
10799 Roo.CompositeElement = function(els){
10800     this.elements = [];
10801     this.addElements(els);
10802 };
10803 Roo.CompositeElement.prototype = {
10804     isComposite: true,
10805     addElements : function(els){
10806         if(!els) return this;
10807         if(typeof els == "string"){
10808             els = Roo.Element.selectorFunction(els);
10809         }
10810         var yels = this.elements;
10811         var index = yels.length-1;
10812         for(var i = 0, len = els.length; i < len; i++) {
10813                 yels[++index] = Roo.get(els[i]);
10814         }
10815         return this;
10816     },
10817
10818     /**
10819     * Clears this composite and adds the elements returned by the passed selector.
10820     * @param {String/Array} els A string CSS selector, an array of elements or an element
10821     * @return {CompositeElement} this
10822     */
10823     fill : function(els){
10824         this.elements = [];
10825         this.add(els);
10826         return this;
10827     },
10828
10829     /**
10830     * Filters this composite to only elements that match the passed selector.
10831     * @param {String} selector A string CSS selector
10832     * @return {CompositeElement} this
10833     */
10834     filter : function(selector){
10835         var els = [];
10836         this.each(function(el){
10837             if(el.is(selector)){
10838                 els[els.length] = el.dom;
10839             }
10840         });
10841         this.fill(els);
10842         return this;
10843     },
10844
10845     invoke : function(fn, args){
10846         var els = this.elements;
10847         for(var i = 0, len = els.length; i < len; i++) {
10848                 Roo.Element.prototype[fn].apply(els[i], args);
10849         }
10850         return this;
10851     },
10852     /**
10853     * Adds elements to this composite.
10854     * @param {String/Array} els A string CSS selector, an array of elements or an element
10855     * @return {CompositeElement} this
10856     */
10857     add : function(els){
10858         if(typeof els == "string"){
10859             this.addElements(Roo.Element.selectorFunction(els));
10860         }else if(els.length !== undefined){
10861             this.addElements(els);
10862         }else{
10863             this.addElements([els]);
10864         }
10865         return this;
10866     },
10867     /**
10868     * Calls the passed function passing (el, this, index) for each element in this composite.
10869     * @param {Function} fn The function to call
10870     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
10871     * @return {CompositeElement} this
10872     */
10873     each : function(fn, scope){
10874         var els = this.elements;
10875         for(var i = 0, len = els.length; i < len; i++){
10876             if(fn.call(scope || els[i], els[i], this, i) === false) {
10877                 break;
10878             }
10879         }
10880         return this;
10881     },
10882
10883     /**
10884      * Returns the Element object at the specified index
10885      * @param {Number} index
10886      * @return {Roo.Element}
10887      */
10888     item : function(index){
10889         return this.elements[index] || null;
10890     },
10891
10892     /**
10893      * Returns the first Element
10894      * @return {Roo.Element}
10895      */
10896     first : function(){
10897         return this.item(0);
10898     },
10899
10900     /**
10901      * Returns the last Element
10902      * @return {Roo.Element}
10903      */
10904     last : function(){
10905         return this.item(this.elements.length-1);
10906     },
10907
10908     /**
10909      * Returns the number of elements in this composite
10910      * @return Number
10911      */
10912     getCount : function(){
10913         return this.elements.length;
10914     },
10915
10916     /**
10917      * Returns true if this composite contains the passed element
10918      * @return Boolean
10919      */
10920     contains : function(el){
10921         return this.indexOf(el) !== -1;
10922     },
10923
10924     /**
10925      * Returns true if this composite contains the passed element
10926      * @return Boolean
10927      */
10928     indexOf : function(el){
10929         return this.elements.indexOf(Roo.get(el));
10930     },
10931
10932
10933     /**
10934     * Removes the specified element(s).
10935     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
10936     * or an array of any of those.
10937     * @param {Boolean} removeDom (optional) True to also remove the element from the document
10938     * @return {CompositeElement} this
10939     */
10940     removeElement : function(el, removeDom){
10941         if(el instanceof Array){
10942             for(var i = 0, len = el.length; i < len; i++){
10943                 this.removeElement(el[i]);
10944             }
10945             return this;
10946         }
10947         var index = typeof el == 'number' ? el : this.indexOf(el);
10948         if(index !== -1){
10949             if(removeDom){
10950                 var d = this.elements[index];
10951                 if(d.dom){
10952                     d.remove();
10953                 }else{
10954                     d.parentNode.removeChild(d);
10955                 }
10956             }
10957             this.elements.splice(index, 1);
10958         }
10959         return this;
10960     },
10961
10962     /**
10963     * Replaces the specified element with the passed element.
10964     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
10965     * to replace.
10966     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
10967     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
10968     * @return {CompositeElement} this
10969     */
10970     replaceElement : function(el, replacement, domReplace){
10971         var index = typeof el == 'number' ? el : this.indexOf(el);
10972         if(index !== -1){
10973             if(domReplace){
10974                 this.elements[index].replaceWith(replacement);
10975             }else{
10976                 this.elements.splice(index, 1, Roo.get(replacement))
10977             }
10978         }
10979         return this;
10980     },
10981
10982     /**
10983      * Removes all elements.
10984      */
10985     clear : function(){
10986         this.elements = [];
10987     }
10988 };
10989 (function(){
10990     Roo.CompositeElement.createCall = function(proto, fnName){
10991         if(!proto[fnName]){
10992             proto[fnName] = function(){
10993                 return this.invoke(fnName, arguments);
10994             };
10995         }
10996     };
10997     for(var fnName in Roo.Element.prototype){
10998         if(typeof Roo.Element.prototype[fnName] == "function"){
10999             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11000         }
11001     };
11002 })();
11003 /*
11004  * Based on:
11005  * Ext JS Library 1.1.1
11006  * Copyright(c) 2006-2007, Ext JS, LLC.
11007  *
11008  * Originally Released Under LGPL - original licence link has changed is not relivant.
11009  *
11010  * Fork - LGPL
11011  * <script type="text/javascript">
11012  */
11013
11014 /**
11015  * @class Roo.CompositeElementLite
11016  * @extends Roo.CompositeElement
11017  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11018  <pre><code>
11019  var els = Roo.select("#some-el div.some-class");
11020  // or select directly from an existing element
11021  var el = Roo.get('some-el');
11022  el.select('div.some-class');
11023
11024  els.setWidth(100); // all elements become 100 width
11025  els.hide(true); // all elements fade out and hide
11026  // or
11027  els.setWidth(100).hide(true);
11028  </code></pre><br><br>
11029  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11030  * actions will be performed on all the elements in this collection.</b>
11031  */
11032 Roo.CompositeElementLite = function(els){
11033     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11034     this.el = new Roo.Element.Flyweight();
11035 };
11036 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11037     addElements : function(els){
11038         if(els){
11039             if(els instanceof Array){
11040                 this.elements = this.elements.concat(els);
11041             }else{
11042                 var yels = this.elements;
11043                 var index = yels.length-1;
11044                 for(var i = 0, len = els.length; i < len; i++) {
11045                     yels[++index] = els[i];
11046                 }
11047             }
11048         }
11049         return this;
11050     },
11051     invoke : function(fn, args){
11052         var els = this.elements;
11053         var el = this.el;
11054         for(var i = 0, len = els.length; i < len; i++) {
11055             el.dom = els[i];
11056                 Roo.Element.prototype[fn].apply(el, args);
11057         }
11058         return this;
11059     },
11060     /**
11061      * Returns a flyweight Element of the dom element object at the specified index
11062      * @param {Number} index
11063      * @return {Roo.Element}
11064      */
11065     item : function(index){
11066         if(!this.elements[index]){
11067             return null;
11068         }
11069         this.el.dom = this.elements[index];
11070         return this.el;
11071     },
11072
11073     // fixes scope with flyweight
11074     addListener : function(eventName, handler, scope, opt){
11075         var els = this.elements;
11076         for(var i = 0, len = els.length; i < len; i++) {
11077             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11078         }
11079         return this;
11080     },
11081
11082     /**
11083     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11084     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11085     * a reference to the dom node, use el.dom.</b>
11086     * @param {Function} fn The function to call
11087     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11088     * @return {CompositeElement} this
11089     */
11090     each : function(fn, scope){
11091         var els = this.elements;
11092         var el = this.el;
11093         for(var i = 0, len = els.length; i < len; i++){
11094             el.dom = els[i];
11095                 if(fn.call(scope || el, el, this, i) === false){
11096                 break;
11097             }
11098         }
11099         return this;
11100     },
11101
11102     indexOf : function(el){
11103         return this.elements.indexOf(Roo.getDom(el));
11104     },
11105
11106     replaceElement : function(el, replacement, domReplace){
11107         var index = typeof el == 'number' ? el : this.indexOf(el);
11108         if(index !== -1){
11109             replacement = Roo.getDom(replacement);
11110             if(domReplace){
11111                 var d = this.elements[index];
11112                 d.parentNode.insertBefore(replacement, d);
11113                 d.parentNode.removeChild(d);
11114             }
11115             this.elements.splice(index, 1, replacement);
11116         }
11117         return this;
11118     }
11119 });
11120 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11121
11122 /*
11123  * Based on:
11124  * Ext JS Library 1.1.1
11125  * Copyright(c) 2006-2007, Ext JS, LLC.
11126  *
11127  * Originally Released Under LGPL - original licence link has changed is not relivant.
11128  *
11129  * Fork - LGPL
11130  * <script type="text/javascript">
11131  */
11132
11133  
11134
11135 /**
11136  * @class Roo.data.Connection
11137  * @extends Roo.util.Observable
11138  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11139  * either to a configured URL, or to a URL specified at request time.<br><br>
11140  * <p>
11141  * Requests made by this class are asynchronous, and will return immediately. No data from
11142  * the server will be available to the statement immediately following the {@link #request} call.
11143  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
11144  * <p>
11145  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11146  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11147  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11148  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
11149  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11150  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11151  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11152  * standard DOM methods.
11153  * @constructor
11154  * @param {Object} config a configuration object.
11155  */
11156 Roo.data.Connection = function(config){
11157     Roo.apply(this, config);
11158     this.addEvents({
11159         /**
11160          * @event beforerequest
11161          * Fires before a network request is made to retrieve a data object.
11162          * @param {Connection} conn This Connection object.
11163          * @param {Object} options The options config object passed to the {@link #request} method.
11164          */
11165         "beforerequest" : true,
11166         /**
11167          * @event requestcomplete
11168          * Fires if the request was successfully completed.
11169          * @param {Connection} conn This Connection object.
11170          * @param {Object} response The XHR object containing the response data.
11171          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11172          * @param {Object} options The options config object passed to the {@link #request} method.
11173          */
11174         "requestcomplete" : true,
11175         /**
11176          * @event requestexception
11177          * Fires if an error HTTP status was returned from the server.
11178          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11179          * @param {Connection} conn This Connection object.
11180          * @param {Object} response The XHR object containing the response data.
11181          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11182          * @param {Object} options The options config object passed to the {@link #request} method.
11183          */
11184         "requestexception" : true
11185     });
11186     Roo.data.Connection.superclass.constructor.call(this);
11187 };
11188
11189 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11190     /**
11191      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11192      */
11193     /**
11194      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11195      * extra parameters to each request made by this object. (defaults to undefined)
11196      */
11197     /**
11198      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11199      *  to each request made by this object. (defaults to undefined)
11200      */
11201     /**
11202      * @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)
11203      */
11204     /**
11205      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11206      */
11207     timeout : 30000,
11208     /**
11209      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11210      * @type Boolean
11211      */
11212     autoAbort:false,
11213
11214     /**
11215      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11216      * @type Boolean
11217      */
11218     disableCaching: true,
11219
11220     /**
11221      * Sends an HTTP request to a remote server.
11222      * @param {Object} options An object which may contain the following properties:<ul>
11223      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11224      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11225      * request, a url encoded string or a function to call to get either.</li>
11226      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11227      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11228      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11229      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11230      * <li>options {Object} The parameter to the request call.</li>
11231      * <li>success {Boolean} True if the request succeeded.</li>
11232      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11233      * </ul></li>
11234      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11235      * The callback is passed the following parameters:<ul>
11236      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11237      * <li>options {Object} The parameter to the request call.</li>
11238      * </ul></li>
11239      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11240      * The callback is passed the following parameters:<ul>
11241      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11242      * <li>options {Object} The parameter to the request call.</li>
11243      * </ul></li>
11244      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11245      * for the callback function. Defaults to the browser window.</li>
11246      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11247      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11248      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11249      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11250      * params for the post data. Any params will be appended to the URL.</li>
11251      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11252      * </ul>
11253      * @return {Number} transactionId
11254      */
11255     request : function(o){
11256         if(this.fireEvent("beforerequest", this, o) !== false){
11257             var p = o.params;
11258
11259             if(typeof p == "function"){
11260                 p = p.call(o.scope||window, o);
11261             }
11262             if(typeof p == "object"){
11263                 p = Roo.urlEncode(o.params);
11264             }
11265             if(this.extraParams){
11266                 var extras = Roo.urlEncode(this.extraParams);
11267                 p = p ? (p + '&' + extras) : extras;
11268             }
11269
11270             var url = o.url || this.url;
11271             if(typeof url == 'function'){
11272                 url = url.call(o.scope||window, o);
11273             }
11274
11275             if(o.form){
11276                 var form = Roo.getDom(o.form);
11277                 url = url || form.action;
11278
11279                 var enctype = form.getAttribute("enctype");
11280                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11281                     return this.doFormUpload(o, p, url);
11282                 }
11283                 var f = Roo.lib.Ajax.serializeForm(form);
11284                 p = p ? (p + '&' + f) : f;
11285             }
11286
11287             var hs = o.headers;
11288             if(this.defaultHeaders){
11289                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11290                 if(!o.headers){
11291                     o.headers = hs;
11292                 }
11293             }
11294
11295             var cb = {
11296                 success: this.handleResponse,
11297                 failure: this.handleFailure,
11298                 scope: this,
11299                 argument: {options: o},
11300                 timeout : this.timeout
11301             };
11302
11303             var method = o.method||this.method||(p ? "POST" : "GET");
11304
11305             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11306                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11307             }
11308
11309             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11310                 if(o.autoAbort){
11311                     this.abort();
11312                 }
11313             }else if(this.autoAbort !== false){
11314                 this.abort();
11315             }
11316
11317             if((method == 'GET' && p) || o.xmlData){
11318                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11319                 p = '';
11320             }
11321             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11322             return this.transId;
11323         }else{
11324             Roo.callback(o.callback, o.scope, [o, null, null]);
11325             return null;
11326         }
11327     },
11328
11329     /**
11330      * Determine whether this object has a request outstanding.
11331      * @param {Number} transactionId (Optional) defaults to the last transaction
11332      * @return {Boolean} True if there is an outstanding request.
11333      */
11334     isLoading : function(transId){
11335         if(transId){
11336             return Roo.lib.Ajax.isCallInProgress(transId);
11337         }else{
11338             return this.transId ? true : false;
11339         }
11340     },
11341
11342     /**
11343      * Aborts any outstanding request.
11344      * @param {Number} transactionId (Optional) defaults to the last transaction
11345      */
11346     abort : function(transId){
11347         if(transId || this.isLoading()){
11348             Roo.lib.Ajax.abort(transId || this.transId);
11349         }
11350     },
11351
11352     // private
11353     handleResponse : function(response){
11354         this.transId = false;
11355         var options = response.argument.options;
11356         response.argument = options ? options.argument : null;
11357         this.fireEvent("requestcomplete", this, response, options);
11358         Roo.callback(options.success, options.scope, [response, options]);
11359         Roo.callback(options.callback, options.scope, [options, true, response]);
11360     },
11361
11362     // private
11363     handleFailure : function(response, e){
11364         this.transId = false;
11365         var options = response.argument.options;
11366         response.argument = options ? options.argument : null;
11367         this.fireEvent("requestexception", this, response, options, e);
11368         Roo.callback(options.failure, options.scope, [response, options]);
11369         Roo.callback(options.callback, options.scope, [options, false, response]);
11370     },
11371
11372     // private
11373     doFormUpload : function(o, ps, url){
11374         var id = Roo.id();
11375         var frame = document.createElement('iframe');
11376         frame.id = id;
11377         frame.name = id;
11378         frame.className = 'x-hidden';
11379         if(Roo.isIE){
11380             frame.src = Roo.SSL_SECURE_URL;
11381         }
11382         document.body.appendChild(frame);
11383
11384         if(Roo.isIE){
11385            document.frames[id].name = id;
11386         }
11387
11388         var form = Roo.getDom(o.form);
11389         form.target = id;
11390         form.method = 'POST';
11391         form.enctype = form.encoding = 'multipart/form-data';
11392         if(url){
11393             form.action = url;
11394         }
11395
11396         var hiddens, hd;
11397         if(ps){ // add dynamic params
11398             hiddens = [];
11399             ps = Roo.urlDecode(ps, false);
11400             for(var k in ps){
11401                 if(ps.hasOwnProperty(k)){
11402                     hd = document.createElement('input');
11403                     hd.type = 'hidden';
11404                     hd.name = k;
11405                     hd.value = ps[k];
11406                     form.appendChild(hd);
11407                     hiddens.push(hd);
11408                 }
11409             }
11410         }
11411
11412         function cb(){
11413             var r = {  // bogus response object
11414                 responseText : '',
11415                 responseXML : null
11416             };
11417
11418             r.argument = o ? o.argument : null;
11419
11420             try { //
11421                 var doc;
11422                 if(Roo.isIE){
11423                     doc = frame.contentWindow.document;
11424                 }else {
11425                     doc = (frame.contentDocument || window.frames[id].document);
11426                 }
11427                 if(doc && doc.body){
11428                     r.responseText = doc.body.innerHTML;
11429                 }
11430                 if(doc && doc.XMLDocument){
11431                     r.responseXML = doc.XMLDocument;
11432                 }else {
11433                     r.responseXML = doc;
11434                 }
11435             }
11436             catch(e) {
11437                 // ignore
11438             }
11439
11440             Roo.EventManager.removeListener(frame, 'load', cb, this);
11441
11442             this.fireEvent("requestcomplete", this, r, o);
11443             Roo.callback(o.success, o.scope, [r, o]);
11444             Roo.callback(o.callback, o.scope, [o, true, r]);
11445
11446             setTimeout(function(){document.body.removeChild(frame);}, 100);
11447         }
11448
11449         Roo.EventManager.on(frame, 'load', cb, this);
11450         form.submit();
11451
11452         if(hiddens){ // remove dynamic params
11453             for(var i = 0, len = hiddens.length; i < len; i++){
11454                 form.removeChild(hiddens[i]);
11455             }
11456         }
11457     }
11458 });
11459
11460 /**
11461  * @class Roo.Ajax
11462  * @extends Roo.data.Connection
11463  * Global Ajax request class.
11464  *
11465  * @singleton
11466  */
11467 Roo.Ajax = new Roo.data.Connection({
11468     // fix up the docs
11469    /**
11470      * @cfg {String} url @hide
11471      */
11472     /**
11473      * @cfg {Object} extraParams @hide
11474      */
11475     /**
11476      * @cfg {Object} defaultHeaders @hide
11477      */
11478     /**
11479      * @cfg {String} method (Optional) @hide
11480      */
11481     /**
11482      * @cfg {Number} timeout (Optional) @hide
11483      */
11484     /**
11485      * @cfg {Boolean} autoAbort (Optional) @hide
11486      */
11487
11488     /**
11489      * @cfg {Boolean} disableCaching (Optional) @hide
11490      */
11491
11492     /**
11493      * @property  disableCaching
11494      * True to add a unique cache-buster param to GET requests. (defaults to true)
11495      * @type Boolean
11496      */
11497     /**
11498      * @property  url
11499      * The default URL to be used for requests to the server. (defaults to undefined)
11500      * @type String
11501      */
11502     /**
11503      * @property  extraParams
11504      * An object containing properties which are used as
11505      * extra parameters to each request made by this object. (defaults to undefined)
11506      * @type Object
11507      */
11508     /**
11509      * @property  defaultHeaders
11510      * An object containing request headers which are added to each request made by this object. (defaults to undefined)
11511      * @type Object
11512      */
11513     /**
11514      * @property  method
11515      * The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11516      * @type String
11517      */
11518     /**
11519      * @property  timeout
11520      * The timeout in milliseconds to be used for requests. (defaults to 30000)
11521      * @type Number
11522      */
11523
11524     /**
11525      * @property  autoAbort
11526      * Whether a new request should abort any pending requests. (defaults to false)
11527      * @type Boolean
11528      */
11529     autoAbort : false,
11530
11531     /**
11532      * Serialize the passed form into a url encoded string
11533      * @param {String/HTMLElement} form
11534      * @return {String}
11535      */
11536     serializeForm : function(form){
11537         return Roo.lib.Ajax.serializeForm(form);
11538     }
11539 });/*
11540  * Based on:
11541  * Ext JS Library 1.1.1
11542  * Copyright(c) 2006-2007, Ext JS, LLC.
11543  *
11544  * Originally Released Under LGPL - original licence link has changed is not relivant.
11545  *
11546  * Fork - LGPL
11547  * <script type="text/javascript">
11548  */
11549  
11550 /**
11551  * @class Roo.Ajax
11552  * @extends Roo.data.Connection
11553  * Global Ajax request class.
11554  *
11555  * @instanceOf  Roo.data.Connection
11556  */
11557 Roo.Ajax = new Roo.data.Connection({
11558     // fix up the docs
11559     
11560     /**
11561      * fix up scoping
11562      * @scope Roo.Ajax
11563      */
11564     
11565    /**
11566      * @cfg {String} url @hide
11567      */
11568     /**
11569      * @cfg {Object} extraParams @hide
11570      */
11571     /**
11572      * @cfg {Object} defaultHeaders @hide
11573      */
11574     /**
11575      * @cfg {String} method (Optional) @hide
11576      */
11577     /**
11578      * @cfg {Number} timeout (Optional) @hide
11579      */
11580     /**
11581      * @cfg {Boolean} autoAbort (Optional) @hide
11582      */
11583
11584     /**
11585      * @cfg {Boolean} disableCaching (Optional) @hide
11586      */
11587
11588     /**
11589      * @property  disableCaching
11590      * True to add a unique cache-buster param to GET requests. (defaults to true)
11591      * @type Boolean
11592      */
11593     /**
11594      * @property  url
11595      * The default URL to be used for requests to the server. (defaults to undefined)
11596      * @type String
11597      */
11598     /**
11599      * @property  extraParams
11600      * An object containing properties which are used as
11601      * extra parameters to each request made by this object. (defaults to undefined)
11602      * @type Object
11603      */
11604     /**
11605      * @property  defaultHeaders
11606      * An object containing request headers which are added to each request made by this object. (defaults to undefined)
11607      * @type Object
11608      */
11609     /**
11610      * @property  method
11611      * The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11612      * @type String
11613      */
11614     /**
11615      * @property  timeout
11616      * The timeout in milliseconds to be used for requests. (defaults to 30000)
11617      * @type Number
11618      */
11619
11620     /**
11621      * @property  autoAbort
11622      * Whether a new request should abort any pending requests. (defaults to false)
11623      * @type Boolean
11624      */
11625     autoAbort : false,
11626
11627     /**
11628      * Serialize the passed form into a url encoded string
11629      * @param {String/HTMLElement} form
11630      * @return {String}
11631      */
11632     serializeForm : function(form){
11633         return Roo.lib.Ajax.serializeForm(form);
11634     }
11635 });/*
11636  * Based on:
11637  * Ext JS Library 1.1.1
11638  * Copyright(c) 2006-2007, Ext JS, LLC.
11639  *
11640  * Originally Released Under LGPL - original licence link has changed is not relivant.
11641  *
11642  * Fork - LGPL
11643  * <script type="text/javascript">
11644  */
11645
11646  
11647 /**
11648  * @class Roo.UpdateManager
11649  * @extends Roo.util.Observable
11650  * Provides AJAX-style update for Element object.<br><br>
11651  * Usage:<br>
11652  * <pre><code>
11653  * // Get it from a Roo.Element object
11654  * var el = Roo.get("foo");
11655  * var mgr = el.getUpdateManager();
11656  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11657  * ...
11658  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11659  * <br>
11660  * // or directly (returns the same UpdateManager instance)
11661  * var mgr = new Roo.UpdateManager("myElementId");
11662  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11663  * mgr.on("update", myFcnNeedsToKnow);
11664  * <br>
11665    // short handed call directly from the element object
11666    Roo.get("foo").load({
11667         url: "bar.php",
11668         scripts:true,
11669         params: "for=bar",
11670         text: "Loading Foo..."
11671    });
11672  * </code></pre>
11673  * @constructor
11674  * Create new UpdateManager directly.
11675  * @param {String/HTMLElement/Roo.Element} el The element to update
11676  * @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).
11677  */
11678 Roo.UpdateManager = function(el, forceNew){
11679     el = Roo.get(el);
11680     if(!forceNew && el.updateManager){
11681         return el.updateManager;
11682     }
11683     /**
11684      * The Element object
11685      * @type Roo.Element
11686      */
11687     this.el = el;
11688     /**
11689      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11690      * @type String
11691      */
11692     this.defaultUrl = null;
11693
11694     this.addEvents({
11695         /**
11696          * @event beforeupdate
11697          * Fired before an update is made, return false from your handler and the update is cancelled.
11698          * @param {Roo.Element} el
11699          * @param {String/Object/Function} url
11700          * @param {String/Object} params
11701          */
11702         "beforeupdate": true,
11703         /**
11704          * @event update
11705          * Fired after successful update is made.
11706          * @param {Roo.Element} el
11707          * @param {Object} oResponseObject The response Object
11708          */
11709         "update": true,
11710         /**
11711          * @event failure
11712          * Fired on update failure.
11713          * @param {Roo.Element} el
11714          * @param {Object} oResponseObject The response Object
11715          */
11716         "failure": true
11717     });
11718     var d = Roo.UpdateManager.defaults;
11719     /**
11720      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11721      * @type String
11722      */
11723     this.sslBlankUrl = d.sslBlankUrl;
11724     /**
11725      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11726      * @type Boolean
11727      */
11728     this.disableCaching = d.disableCaching;
11729     /**
11730      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11731      * @type String
11732      */
11733     this.indicatorText = d.indicatorText;
11734     /**
11735      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11736      * @type String
11737      */
11738     this.showLoadIndicator = d.showLoadIndicator;
11739     /**
11740      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11741      * @type Number
11742      */
11743     this.timeout = d.timeout;
11744
11745     /**
11746      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11747      * @type Boolean
11748      */
11749     this.loadScripts = d.loadScripts;
11750
11751     /**
11752      * Transaction object of current executing transaction
11753      */
11754     this.transaction = null;
11755
11756     /**
11757      * @private
11758      */
11759     this.autoRefreshProcId = null;
11760     /**
11761      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11762      * @type Function
11763      */
11764     this.refreshDelegate = this.refresh.createDelegate(this);
11765     /**
11766      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11767      * @type Function
11768      */
11769     this.updateDelegate = this.update.createDelegate(this);
11770     /**
11771      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11772      * @type Function
11773      */
11774     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11775     /**
11776      * @private
11777      */
11778     this.successDelegate = this.processSuccess.createDelegate(this);
11779     /**
11780      * @private
11781      */
11782     this.failureDelegate = this.processFailure.createDelegate(this);
11783
11784     if(!this.renderer){
11785      /**
11786       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11787       */
11788     this.renderer = new Roo.UpdateManager.BasicRenderer();
11789     }
11790     
11791     Roo.UpdateManager.superclass.constructor.call(this);
11792 };
11793
11794 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11795     /**
11796      * Get the Element this UpdateManager is bound to
11797      * @return {Roo.Element} The element
11798      */
11799     getEl : function(){
11800         return this.el;
11801     },
11802     /**
11803      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11804      * @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:
11805 <pre><code>
11806 um.update({<br/>
11807     url: "your-url.php",<br/>
11808     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11809     callback: yourFunction,<br/>
11810     scope: yourObject, //(optional scope)  <br/>
11811     discardUrl: false, <br/>
11812     nocache: false,<br/>
11813     text: "Loading...",<br/>
11814     timeout: 30,<br/>
11815     scripts: false<br/>
11816 });
11817 </code></pre>
11818      * The only required property is url. The optional properties nocache, text and scripts
11819      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11820      * @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}
11821      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11822      * @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.
11823      */
11824     update : function(url, params, callback, discardUrl){
11825         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11826             var method = this.method, cfg;
11827             if(typeof url == "object"){ // must be config object
11828                 cfg = url;
11829                 url = cfg.url;
11830                 params = params || cfg.params;
11831                 callback = callback || cfg.callback;
11832                 discardUrl = discardUrl || cfg.discardUrl;
11833                 if(callback && cfg.scope){
11834                     callback = callback.createDelegate(cfg.scope);
11835                 }
11836                 if(typeof cfg.method != "undefined"){method = cfg.method;};
11837                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
11838                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
11839                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
11840                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
11841             }
11842             this.showLoading();
11843             if(!discardUrl){
11844                 this.defaultUrl = url;
11845             }
11846             if(typeof url == "function"){
11847                 url = url.call(this);
11848             }
11849
11850             method = method || (params ? "POST" : "GET");
11851             if(method == "GET"){
11852                 url = this.prepareUrl(url);
11853             }
11854
11855             var o = Roo.apply(cfg ||{}, {
11856                 url : url,
11857                 params: params,
11858                 success: this.successDelegate,
11859                 failure: this.failureDelegate,
11860                 callback: undefined,
11861                 timeout: (this.timeout*1000),
11862                 argument: {"url": url, "form": null, "callback": callback, "params": params}
11863             });
11864
11865             this.transaction = Roo.Ajax.request(o);
11866         }
11867     },
11868
11869     /**
11870      * 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.
11871      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
11872      * @param {String/HTMLElement} form The form Id or form element
11873      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
11874      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
11875      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11876      */
11877     formUpdate : function(form, url, reset, callback){
11878         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
11879             if(typeof url == "function"){
11880                 url = url.call(this);
11881             }
11882             form = Roo.getDom(form);
11883             this.transaction = Roo.Ajax.request({
11884                 form: form,
11885                 url:url,
11886                 success: this.successDelegate,
11887                 failure: this.failureDelegate,
11888                 timeout: (this.timeout*1000),
11889                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
11890             });
11891             this.showLoading.defer(1, this);
11892         }
11893     },
11894
11895     /**
11896      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
11897      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11898      */
11899     refresh : function(callback){
11900         if(this.defaultUrl == null){
11901             return;
11902         }
11903         this.update(this.defaultUrl, null, callback, true);
11904     },
11905
11906     /**
11907      * Set this element to auto refresh.
11908      * @param {Number} interval How often to update (in seconds).
11909      * @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)
11910      * @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}
11911      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11912      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
11913      */
11914     startAutoRefresh : function(interval, url, params, callback, refreshNow){
11915         if(refreshNow){
11916             this.update(url || this.defaultUrl, params, callback, true);
11917         }
11918         if(this.autoRefreshProcId){
11919             clearInterval(this.autoRefreshProcId);
11920         }
11921         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
11922     },
11923
11924     /**
11925      * Stop auto refresh on this element.
11926      */
11927      stopAutoRefresh : function(){
11928         if(this.autoRefreshProcId){
11929             clearInterval(this.autoRefreshProcId);
11930             delete this.autoRefreshProcId;
11931         }
11932     },
11933
11934     isAutoRefreshing : function(){
11935        return this.autoRefreshProcId ? true : false;
11936     },
11937     /**
11938      * Called to update the element to "Loading" state. Override to perform custom action.
11939      */
11940     showLoading : function(){
11941         if(this.showLoadIndicator){
11942             this.el.update(this.indicatorText);
11943         }
11944     },
11945
11946     /**
11947      * Adds unique parameter to query string if disableCaching = true
11948      * @private
11949      */
11950     prepareUrl : function(url){
11951         if(this.disableCaching){
11952             var append = "_dc=" + (new Date().getTime());
11953             if(url.indexOf("?") !== -1){
11954                 url += "&" + append;
11955             }else{
11956                 url += "?" + append;
11957             }
11958         }
11959         return url;
11960     },
11961
11962     /**
11963      * @private
11964      */
11965     processSuccess : function(response){
11966         this.transaction = null;
11967         if(response.argument.form && response.argument.reset){
11968             try{ // put in try/catch since some older FF releases had problems with this
11969                 response.argument.form.reset();
11970             }catch(e){}
11971         }
11972         if(this.loadScripts){
11973             this.renderer.render(this.el, response, this,
11974                 this.updateComplete.createDelegate(this, [response]));
11975         }else{
11976             this.renderer.render(this.el, response, this);
11977             this.updateComplete(response);
11978         }
11979     },
11980
11981     updateComplete : function(response){
11982         this.fireEvent("update", this.el, response);
11983         if(typeof response.argument.callback == "function"){
11984             response.argument.callback(this.el, true, response);
11985         }
11986     },
11987
11988     /**
11989      * @private
11990      */
11991     processFailure : function(response){
11992         this.transaction = null;
11993         this.fireEvent("failure", this.el, response);
11994         if(typeof response.argument.callback == "function"){
11995             response.argument.callback(this.el, false, response);
11996         }
11997     },
11998
11999     /**
12000      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
12001      * @param {Object} renderer The object implementing the render() method
12002      */
12003     setRenderer : function(renderer){
12004         this.renderer = renderer;
12005     },
12006
12007     getRenderer : function(){
12008        return this.renderer;
12009     },
12010
12011     /**
12012      * Set the defaultUrl used for updates
12013      * @param {String/Function} defaultUrl The url or a function to call to get the url
12014      */
12015     setDefaultUrl : function(defaultUrl){
12016         this.defaultUrl = defaultUrl;
12017     },
12018
12019     /**
12020      * Aborts the executing transaction
12021      */
12022     abort : function(){
12023         if(this.transaction){
12024             Roo.Ajax.abort(this.transaction);
12025         }
12026     },
12027
12028     /**
12029      * Returns true if an update is in progress
12030      * @return {Boolean}
12031      */
12032     isUpdating : function(){
12033         if(this.transaction){
12034             return Roo.Ajax.isLoading(this.transaction);
12035         }
12036         return false;
12037     }
12038 });
12039
12040 /**
12041  * @class Roo.UpdateManager.defaults
12042  * @static (not really - but it helps the doc tool)
12043  * The defaults collection enables customizing the default properties of UpdateManager
12044  */
12045    Roo.UpdateManager.defaults = {
12046        /**
12047          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12048          * @type Number
12049          */
12050          timeout : 30,
12051
12052          /**
12053          * True to process scripts by default (Defaults to false).
12054          * @type Boolean
12055          */
12056         loadScripts : false,
12057
12058         /**
12059         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12060         * @type String
12061         */
12062         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12063         /**
12064          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12065          * @type Boolean
12066          */
12067         disableCaching : false,
12068         /**
12069          * Whether to show indicatorText when loading (Defaults to true).
12070          * @type Boolean
12071          */
12072         showLoadIndicator : true,
12073         /**
12074          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12075          * @type String
12076          */
12077         indicatorText : '<div class="loading-indicator">Loading...</div>'
12078    };
12079
12080 /**
12081  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12082  *Usage:
12083  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12084  * @param {String/HTMLElement/Roo.Element} el The element to update
12085  * @param {String} url The url
12086  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12087  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12088  * @static
12089  * @deprecated
12090  * @member Roo.UpdateManager
12091  */
12092 Roo.UpdateManager.updateElement = function(el, url, params, options){
12093     var um = Roo.get(el, true).getUpdateManager();
12094     Roo.apply(um, options);
12095     um.update(url, params, options ? options.callback : null);
12096 };
12097 // alias for backwards compat
12098 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12099 /**
12100  * @class Roo.UpdateManager.BasicRenderer
12101  * Default Content renderer. Updates the elements innerHTML with the responseText.
12102  */
12103 Roo.UpdateManager.BasicRenderer = function(){};
12104
12105 Roo.UpdateManager.BasicRenderer.prototype = {
12106     /**
12107      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12108      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12109      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12110      * @param {Roo.Element} el The element being rendered
12111      * @param {Object} response The YUI Connect response object
12112      * @param {UpdateManager} updateManager The calling update manager
12113      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12114      */
12115      render : function(el, response, updateManager, callback){
12116         el.update(response.responseText, updateManager.loadScripts, callback);
12117     }
12118 };
12119 /*
12120  * Based on:
12121  * Ext JS Library 1.1.1
12122  * Copyright(c) 2006-2007, Ext JS, LLC.
12123  *
12124  * Originally Released Under LGPL - original licence link has changed is not relivant.
12125  *
12126  * Fork - LGPL
12127  * <script type="text/javascript">
12128  */
12129
12130 /**
12131  * @class Roo.util.DelayedTask
12132  * Provides a convenient method of performing setTimeout where a new
12133  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12134  * You can use this class to buffer
12135  * the keypress events for a certain number of milliseconds, and perform only if they stop
12136  * for that amount of time.
12137  * @constructor The parameters to this constructor serve as defaults and are not required.
12138  * @param {Function} fn (optional) The default function to timeout
12139  * @param {Object} scope (optional) The default scope of that timeout
12140  * @param {Array} args (optional) The default Array of arguments
12141  */
12142 Roo.util.DelayedTask = function(fn, scope, args){
12143     var id = null, d, t;
12144
12145     var call = function(){
12146         var now = new Date().getTime();
12147         if(now - t >= d){
12148             clearInterval(id);
12149             id = null;
12150             fn.apply(scope, args || []);
12151         }
12152     };
12153     /**
12154      * Cancels any pending timeout and queues a new one
12155      * @param {Number} delay The milliseconds to delay
12156      * @param {Function} newFn (optional) Overrides function passed to constructor
12157      * @param {Object} newScope (optional) Overrides scope passed to constructor
12158      * @param {Array} newArgs (optional) Overrides args passed to constructor
12159      */
12160     this.delay = function(delay, newFn, newScope, newArgs){
12161         if(id && delay != d){
12162             this.cancel();
12163         }
12164         d = delay;
12165         t = new Date().getTime();
12166         fn = newFn || fn;
12167         scope = newScope || scope;
12168         args = newArgs || args;
12169         if(!id){
12170             id = setInterval(call, d);
12171         }
12172     };
12173
12174     /**
12175      * Cancel the last queued timeout
12176      */
12177     this.cancel = function(){
12178         if(id){
12179             clearInterval(id);
12180             id = null;
12181         }
12182     };
12183 };/*
12184  * Based on:
12185  * Ext JS Library 1.1.1
12186  * Copyright(c) 2006-2007, Ext JS, LLC.
12187  *
12188  * Originally Released Under LGPL - original licence link has changed is not relivant.
12189  *
12190  * Fork - LGPL
12191  * <script type="text/javascript">
12192  */
12193  
12194  
12195 Roo.util.TaskRunner = function(interval){
12196     interval = interval || 10;
12197     var tasks = [], removeQueue = [];
12198     var id = 0;
12199     var running = false;
12200
12201     var stopThread = function(){
12202         running = false;
12203         clearInterval(id);
12204         id = 0;
12205     };
12206
12207     var startThread = function(){
12208         if(!running){
12209             running = true;
12210             id = setInterval(runTasks, interval);
12211         }
12212     };
12213
12214     var removeTask = function(task){
12215         removeQueue.push(task);
12216         if(task.onStop){
12217             task.onStop();
12218         }
12219     };
12220
12221     var runTasks = function(){
12222         if(removeQueue.length > 0){
12223             for(var i = 0, len = removeQueue.length; i < len; i++){
12224                 tasks.remove(removeQueue[i]);
12225             }
12226             removeQueue = [];
12227             if(tasks.length < 1){
12228                 stopThread();
12229                 return;
12230             }
12231         }
12232         var now = new Date().getTime();
12233         for(var i = 0, len = tasks.length; i < len; ++i){
12234             var t = tasks[i];
12235             var itime = now - t.taskRunTime;
12236             if(t.interval <= itime){
12237                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12238                 t.taskRunTime = now;
12239                 if(rt === false || t.taskRunCount === t.repeat){
12240                     removeTask(t);
12241                     return;
12242                 }
12243             }
12244             if(t.duration && t.duration <= (now - t.taskStartTime)){
12245                 removeTask(t);
12246             }
12247         }
12248     };
12249
12250     /**
12251      * Queues a new task.
12252      * @param {Object} task
12253      */
12254     this.start = function(task){
12255         tasks.push(task);
12256         task.taskStartTime = new Date().getTime();
12257         task.taskRunTime = 0;
12258         task.taskRunCount = 0;
12259         startThread();
12260         return task;
12261     };
12262
12263     this.stop = function(task){
12264         removeTask(task);
12265         return task;
12266     };
12267
12268     this.stopAll = function(){
12269         stopThread();
12270         for(var i = 0, len = tasks.length; i < len; i++){
12271             if(tasks[i].onStop){
12272                 tasks[i].onStop();
12273             }
12274         }
12275         tasks = [];
12276         removeQueue = [];
12277     };
12278 };
12279
12280 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12281  * Based on:
12282  * Ext JS Library 1.1.1
12283  * Copyright(c) 2006-2007, Ext JS, LLC.
12284  *
12285  * Originally Released Under LGPL - original licence link has changed is not relivant.
12286  *
12287  * Fork - LGPL
12288  * <script type="text/javascript">
12289  */
12290
12291  
12292 /**
12293  * @class Roo.util.MixedCollection
12294  * @extends Roo.util.Observable
12295  * A Collection class that maintains both numeric indexes and keys and exposes events.
12296  * @constructor
12297  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12298  * collection (defaults to false)
12299  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12300  * and return the key value for that item.  This is used when available to look up the key on items that
12301  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12302  * equivalent to providing an implementation for the {@link #getKey} method.
12303  */
12304 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12305     this.items = [];
12306     this.map = {};
12307     this.keys = [];
12308     this.length = 0;
12309     this.addEvents({
12310         /**
12311          * @event clear
12312          * Fires when the collection is cleared.
12313          */
12314         "clear" : true,
12315         /**
12316          * @event add
12317          * Fires when an item is added to the collection.
12318          * @param {Number} index The index at which the item was added.
12319          * @param {Object} o The item added.
12320          * @param {String} key The key associated with the added item.
12321          */
12322         "add" : true,
12323         /**
12324          * @event replace
12325          * Fires when an item is replaced in the collection.
12326          * @param {String} key he key associated with the new added.
12327          * @param {Object} old The item being replaced.
12328          * @param {Object} new The new item.
12329          */
12330         "replace" : true,
12331         /**
12332          * @event remove
12333          * Fires when an item is removed from the collection.
12334          * @param {Object} o The item being removed.
12335          * @param {String} key (optional) The key associated with the removed item.
12336          */
12337         "remove" : true,
12338         "sort" : true
12339     });
12340     this.allowFunctions = allowFunctions === true;
12341     if(keyFn){
12342         this.getKey = keyFn;
12343     }
12344     Roo.util.MixedCollection.superclass.constructor.call(this);
12345 };
12346
12347 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12348     allowFunctions : false,
12349     
12350 /**
12351  * Adds an item to the collection.
12352  * @param {String} key The key to associate with the item
12353  * @param {Object} o The item to add.
12354  * @return {Object} The item added.
12355  */
12356     add : function(key, o){
12357         if(arguments.length == 1){
12358             o = arguments[0];
12359             key = this.getKey(o);
12360         }
12361         if(typeof key == "undefined" || key === null){
12362             this.length++;
12363             this.items.push(o);
12364             this.keys.push(null);
12365         }else{
12366             var old = this.map[key];
12367             if(old){
12368                 return this.replace(key, o);
12369             }
12370             this.length++;
12371             this.items.push(o);
12372             this.map[key] = o;
12373             this.keys.push(key);
12374         }
12375         this.fireEvent("add", this.length-1, o, key);
12376         return o;
12377     },
12378        
12379 /**
12380   * MixedCollection has a generic way to fetch keys if you implement getKey.
12381 <pre><code>
12382 // normal way
12383 var mc = new Roo.util.MixedCollection();
12384 mc.add(someEl.dom.id, someEl);
12385 mc.add(otherEl.dom.id, otherEl);
12386 //and so on
12387
12388 // using getKey
12389 var mc = new Roo.util.MixedCollection();
12390 mc.getKey = function(el){
12391    return el.dom.id;
12392 };
12393 mc.add(someEl);
12394 mc.add(otherEl);
12395
12396 // or via the constructor
12397 var mc = new Roo.util.MixedCollection(false, function(el){
12398    return el.dom.id;
12399 });
12400 mc.add(someEl);
12401 mc.add(otherEl);
12402 </code></pre>
12403  * @param o {Object} The item for which to find the key.
12404  * @return {Object} The key for the passed item.
12405  */
12406     getKey : function(o){
12407          return o.id; 
12408     },
12409    
12410 /**
12411  * Replaces an item in the collection.
12412  * @param {String} key The key associated with the item to replace, or the item to replace.
12413  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
12414  * @return {Object}  The new item.
12415  */
12416     replace : function(key, o){
12417         if(arguments.length == 1){
12418             o = arguments[0];
12419             key = this.getKey(o);
12420         }
12421         var old = this.item(key);
12422         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
12423              return this.add(key, o);
12424         }
12425         var index = this.indexOfKey(key);
12426         this.items[index] = o;
12427         this.map[key] = o;
12428         this.fireEvent("replace", key, old, o);
12429         return o;
12430     },
12431    
12432 /**
12433  * Adds all elements of an Array or an Object to the collection.
12434  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
12435  * an Array of values, each of which are added to the collection.
12436  */
12437     addAll : function(objs){
12438         if(arguments.length > 1 || objs instanceof Array){
12439             var args = arguments.length > 1 ? arguments : objs;
12440             for(var i = 0, len = args.length; i < len; i++){
12441                 this.add(args[i]);
12442             }
12443         }else{
12444             for(var key in objs){
12445                 if(this.allowFunctions || typeof objs[key] != "function"){
12446                     this.add(key, objs[key]);
12447                 }
12448             }
12449         }
12450     },
12451    
12452 /**
12453  * Executes the specified function once for every item in the collection, passing each
12454  * item as the first and only parameter. returning false from the function will stop the iteration.
12455  * @param {Function} fn The function to execute for each item.
12456  * @param {Object} scope (optional) The scope in which to execute the function.
12457  */
12458     each : function(fn, scope){
12459         var items = [].concat(this.items); // each safe for removal
12460         for(var i = 0, len = items.length; i < len; i++){
12461             if(fn.call(scope || items[i], items[i], i, len) === false){
12462                 break;
12463             }
12464         }
12465     },
12466    
12467 /**
12468  * Executes the specified function once for every key in the collection, passing each
12469  * key, and its associated item as the first two parameters.
12470  * @param {Function} fn The function to execute for each item.
12471  * @param {Object} scope (optional) The scope in which to execute the function.
12472  */
12473     eachKey : function(fn, scope){
12474         for(var i = 0, len = this.keys.length; i < len; i++){
12475             fn.call(scope || window, this.keys[i], this.items[i], i, len);
12476         }
12477     },
12478    
12479 /**
12480  * Returns the first item in the collection which elicits a true return value from the
12481  * passed selection function.
12482  * @param {Function} fn The selection function to execute for each item.
12483  * @param {Object} scope (optional) The scope in which to execute the function.
12484  * @return {Object} The first item in the collection which returned true from the selection function.
12485  */
12486     find : function(fn, scope){
12487         for(var i = 0, len = this.items.length; i < len; i++){
12488             if(fn.call(scope || window, this.items[i], this.keys[i])){
12489                 return this.items[i];
12490             }
12491         }
12492         return null;
12493     },
12494    
12495 /**
12496  * Inserts an item at the specified index in the collection.
12497  * @param {Number} index The index to insert the item at.
12498  * @param {String} key The key to associate with the new item, or the item itself.
12499  * @param {Object} o  (optional) If the second parameter was a key, the new item.
12500  * @return {Object} The item inserted.
12501  */
12502     insert : function(index, key, o){
12503         if(arguments.length == 2){
12504             o = arguments[1];
12505             key = this.getKey(o);
12506         }
12507         if(index >= this.length){
12508             return this.add(key, o);
12509         }
12510         this.length++;
12511         this.items.splice(index, 0, o);
12512         if(typeof key != "undefined" && key != null){
12513             this.map[key] = o;
12514         }
12515         this.keys.splice(index, 0, key);
12516         this.fireEvent("add", index, o, key);
12517         return o;
12518     },
12519    
12520 /**
12521  * Removed an item from the collection.
12522  * @param {Object} o The item to remove.
12523  * @return {Object} The item removed.
12524  */
12525     remove : function(o){
12526         return this.removeAt(this.indexOf(o));
12527     },
12528    
12529 /**
12530  * Remove an item from a specified index in the collection.
12531  * @param {Number} index The index within the collection of the item to remove.
12532  */
12533     removeAt : function(index){
12534         if(index < this.length && index >= 0){
12535             this.length--;
12536             var o = this.items[index];
12537             this.items.splice(index, 1);
12538             var key = this.keys[index];
12539             if(typeof key != "undefined"){
12540                 delete this.map[key];
12541             }
12542             this.keys.splice(index, 1);
12543             this.fireEvent("remove", o, key);
12544         }
12545     },
12546    
12547 /**
12548  * Removed an item associated with the passed key fom the collection.
12549  * @param {String} key The key of the item to remove.
12550  */
12551     removeKey : function(key){
12552         return this.removeAt(this.indexOfKey(key));
12553     },
12554    
12555 /**
12556  * Returns the number of items in the collection.
12557  * @return {Number} the number of items in the collection.
12558  */
12559     getCount : function(){
12560         return this.length; 
12561     },
12562    
12563 /**
12564  * Returns index within the collection of the passed Object.
12565  * @param {Object} o The item to find the index of.
12566  * @return {Number} index of the item.
12567  */
12568     indexOf : function(o){
12569         if(!this.items.indexOf){
12570             for(var i = 0, len = this.items.length; i < len; i++){
12571                 if(this.items[i] == o) return i;
12572             }
12573             return -1;
12574         }else{
12575             return this.items.indexOf(o);
12576         }
12577     },
12578    
12579 /**
12580  * Returns index within the collection of the passed key.
12581  * @param {String} key The key to find the index of.
12582  * @return {Number} index of the key.
12583  */
12584     indexOfKey : function(key){
12585         if(!this.keys.indexOf){
12586             for(var i = 0, len = this.keys.length; i < len; i++){
12587                 if(this.keys[i] == key) return i;
12588             }
12589             return -1;
12590         }else{
12591             return this.keys.indexOf(key);
12592         }
12593     },
12594    
12595 /**
12596  * Returns the item associated with the passed key OR index. Key has priority over index.
12597  * @param {String/Number} key The key or index of the item.
12598  * @return {Object} The item associated with the passed key.
12599  */
12600     item : function(key){
12601         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
12602         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
12603     },
12604     
12605 /**
12606  * Returns the item at the specified index.
12607  * @param {Number} index The index of the item.
12608  * @return {Object}
12609  */
12610     itemAt : function(index){
12611         return this.items[index];
12612     },
12613     
12614 /**
12615  * Returns the item associated with the passed key.
12616  * @param {String/Number} key The key of the item.
12617  * @return {Object} The item associated with the passed key.
12618  */
12619     key : function(key){
12620         return this.map[key];
12621     },
12622    
12623 /**
12624  * Returns true if the collection contains the passed Object as an item.
12625  * @param {Object} o  The Object to look for in the collection.
12626  * @return {Boolean} True if the collection contains the Object as an item.
12627  */
12628     contains : function(o){
12629         return this.indexOf(o) != -1;
12630     },
12631    
12632 /**
12633  * Returns true if the collection contains the passed Object as a key.
12634  * @param {String} key The key to look for in the collection.
12635  * @return {Boolean} True if the collection contains the Object as a key.
12636  */
12637     containsKey : function(key){
12638         return typeof this.map[key] != "undefined";
12639     },
12640    
12641 /**
12642  * Removes all items from the collection.
12643  */
12644     clear : function(){
12645         this.length = 0;
12646         this.items = [];
12647         this.keys = [];
12648         this.map = {};
12649         this.fireEvent("clear");
12650     },
12651    
12652 /**
12653  * Returns the first item in the collection.
12654  * @return {Object} the first item in the collection..
12655  */
12656     first : function(){
12657         return this.items[0]; 
12658     },
12659    
12660 /**
12661  * Returns the last item in the collection.
12662  * @return {Object} the last item in the collection..
12663  */
12664     last : function(){
12665         return this.items[this.length-1];   
12666     },
12667     
12668     _sort : function(property, dir, fn){
12669         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
12670         fn = fn || function(a, b){
12671             return a-b;
12672         };
12673         var c = [], k = this.keys, items = this.items;
12674         for(var i = 0, len = items.length; i < len; i++){
12675             c[c.length] = {key: k[i], value: items[i], index: i};
12676         }
12677         c.sort(function(a, b){
12678             var v = fn(a[property], b[property]) * dsc;
12679             if(v == 0){
12680                 v = (a.index < b.index ? -1 : 1);
12681             }
12682             return v;
12683         });
12684         for(var i = 0, len = c.length; i < len; i++){
12685             items[i] = c[i].value;
12686             k[i] = c[i].key;
12687         }
12688         this.fireEvent("sort", this);
12689     },
12690     
12691     /**
12692      * Sorts this collection with the passed comparison function
12693      * @param {String} direction (optional) "ASC" or "DESC"
12694      * @param {Function} fn (optional) comparison function
12695      */
12696     sort : function(dir, fn){
12697         this._sort("value", dir, fn);
12698     },
12699     
12700     /**
12701      * Sorts this collection by keys
12702      * @param {String} direction (optional) "ASC" or "DESC"
12703      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
12704      */
12705     keySort : function(dir, fn){
12706         this._sort("key", dir, fn || function(a, b){
12707             return String(a).toUpperCase()-String(b).toUpperCase();
12708         });
12709     },
12710     
12711     /**
12712      * Returns a range of items in this collection
12713      * @param {Number} startIndex (optional) defaults to 0
12714      * @param {Number} endIndex (optional) default to the last item
12715      * @return {Array} An array of items
12716      */
12717     getRange : function(start, end){
12718         var items = this.items;
12719         if(items.length < 1){
12720             return [];
12721         }
12722         start = start || 0;
12723         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
12724         var r = [];
12725         if(start <= end){
12726             for(var i = start; i <= end; i++) {
12727                     r[r.length] = items[i];
12728             }
12729         }else{
12730             for(var i = start; i >= end; i--) {
12731                     r[r.length] = items[i];
12732             }
12733         }
12734         return r;
12735     },
12736         
12737     /**
12738      * Filter the <i>objects</i> in this collection by a specific property. 
12739      * Returns a new collection that has been filtered.
12740      * @param {String} property A property on your objects
12741      * @param {String/RegExp} value Either string that the property values 
12742      * should start with or a RegExp to test against the property
12743      * @return {MixedCollection} The new filtered collection
12744      */
12745     filter : function(property, value){
12746         if(!value.exec){ // not a regex
12747             value = String(value);
12748             if(value.length == 0){
12749                 return this.clone();
12750             }
12751             value = new RegExp("^" + Roo.escapeRe(value), "i");
12752         }
12753         return this.filterBy(function(o){
12754             return o && value.test(o[property]);
12755         });
12756         },
12757     
12758     /**
12759      * Filter by a function. * Returns a new collection that has been filtered.
12760      * The passed function will be called with each 
12761      * object in the collection. If the function returns true, the value is included 
12762      * otherwise it is filtered.
12763      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
12764      * @param {Object} scope (optional) The scope of the function (defaults to this) 
12765      * @return {MixedCollection} The new filtered collection
12766      */
12767     filterBy : function(fn, scope){
12768         var r = new Roo.util.MixedCollection();
12769         r.getKey = this.getKey;
12770         var k = this.keys, it = this.items;
12771         for(var i = 0, len = it.length; i < len; i++){
12772             if(fn.call(scope||this, it[i], k[i])){
12773                                 r.add(k[i], it[i]);
12774                         }
12775         }
12776         return r;
12777     },
12778     
12779     /**
12780      * Creates a duplicate of this collection
12781      * @return {MixedCollection}
12782      */
12783     clone : function(){
12784         var r = new Roo.util.MixedCollection();
12785         var k = this.keys, it = this.items;
12786         for(var i = 0, len = it.length; i < len; i++){
12787             r.add(k[i], it[i]);
12788         }
12789         r.getKey = this.getKey;
12790         return r;
12791     }
12792 });
12793 /**
12794  * Returns the item associated with the passed key or index.
12795  * @method
12796  * @param {String/Number} key The key or index of the item.
12797  * @return {Object} The item associated with the passed key.
12798  */
12799 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
12800  * Based on:
12801  * Ext JS Library 1.1.1
12802  * Copyright(c) 2006-2007, Ext JS, LLC.
12803  *
12804  * Originally Released Under LGPL - original licence link has changed is not relivant.
12805  *
12806  * Fork - LGPL
12807  * <script type="text/javascript">
12808  */
12809 /**
12810  * @class Roo.util.JSON
12811  * Modified version of Douglas Crockford"s json.js that doesn"t
12812  * mess with the Object prototype 
12813  * http://www.json.org/js.html
12814  * @singleton
12815  */
12816 Roo.util.JSON = new (function(){
12817     var useHasOwn = {}.hasOwnProperty ? true : false;
12818     
12819     // crashes Safari in some instances
12820     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
12821     
12822     var pad = function(n) {
12823         return n < 10 ? "0" + n : n;
12824     };
12825     
12826     var m = {
12827         "\b": '\\b',
12828         "\t": '\\t',
12829         "\n": '\\n',
12830         "\f": '\\f',
12831         "\r": '\\r',
12832         '"' : '\\"',
12833         "\\": '\\\\'
12834     };
12835
12836     var encodeString = function(s){
12837         if (/["\\\x00-\x1f]/.test(s)) {
12838             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
12839                 var c = m[b];
12840                 if(c){
12841                     return c;
12842                 }
12843                 c = b.charCodeAt();
12844                 return "\\u00" +
12845                     Math.floor(c / 16).toString(16) +
12846                     (c % 16).toString(16);
12847             }) + '"';
12848         }
12849         return '"' + s + '"';
12850     };
12851     
12852     var encodeArray = function(o){
12853         var a = ["["], b, i, l = o.length, v;
12854             for (i = 0; i < l; i += 1) {
12855                 v = o[i];
12856                 switch (typeof v) {
12857                     case "undefined":
12858                     case "function":
12859                     case "unknown":
12860                         break;
12861                     default:
12862                         if (b) {
12863                             a.push(',');
12864                         }
12865                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
12866                         b = true;
12867                 }
12868             }
12869             a.push("]");
12870             return a.join("");
12871     };
12872     
12873     var encodeDate = function(o){
12874         return '"' + o.getFullYear() + "-" +
12875                 pad(o.getMonth() + 1) + "-" +
12876                 pad(o.getDate()) + "T" +
12877                 pad(o.getHours()) + ":" +
12878                 pad(o.getMinutes()) + ":" +
12879                 pad(o.getSeconds()) + '"';
12880     };
12881     
12882     /**
12883      * Encodes an Object, Array or other value
12884      * @param {Mixed} o The variable to encode
12885      * @return {String} The JSON string
12886      */
12887     this.encode = function(o)
12888     {
12889         // should this be extended to fully wrap stringify..
12890         
12891         if(typeof o == "undefined" || o === null){
12892             return "null";
12893         }else if(o instanceof Array){
12894             return encodeArray(o);
12895         }else if(o instanceof Date){
12896             return encodeDate(o);
12897         }else if(typeof o == "string"){
12898             return encodeString(o);
12899         }else if(typeof o == "number"){
12900             return isFinite(o) ? String(o) : "null";
12901         }else if(typeof o == "boolean"){
12902             return String(o);
12903         }else {
12904             var a = ["{"], b, i, v;
12905             for (i in o) {
12906                 if(!useHasOwn || o.hasOwnProperty(i)) {
12907                     v = o[i];
12908                     switch (typeof v) {
12909                     case "undefined":
12910                     case "function":
12911                     case "unknown":
12912                         break;
12913                     default:
12914                         if(b){
12915                             a.push(',');
12916                         }
12917                         a.push(this.encode(i), ":",
12918                                 v === null ? "null" : this.encode(v));
12919                         b = true;
12920                     }
12921                 }
12922             }
12923             a.push("}");
12924             return a.join("");
12925         }
12926     };
12927     
12928     /**
12929      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
12930      * @param {String} json The JSON string
12931      * @return {Object} The resulting object
12932      */
12933     this.decode = function(json){
12934         
12935         return  /** eval:var:json */ eval("(" + json + ')');
12936     };
12937 })();
12938 /** 
12939  * Shorthand for {@link Roo.util.JSON#encode}
12940  * @member Roo encode 
12941  * @method */
12942 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
12943 /** 
12944  * Shorthand for {@link Roo.util.JSON#decode}
12945  * @member Roo decode 
12946  * @method */
12947 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
12948 /*
12949  * Based on:
12950  * Ext JS Library 1.1.1
12951  * Copyright(c) 2006-2007, Ext JS, LLC.
12952  *
12953  * Originally Released Under LGPL - original licence link has changed is not relivant.
12954  *
12955  * Fork - LGPL
12956  * <script type="text/javascript">
12957  */
12958  
12959 /**
12960  * @class Roo.util.Format
12961  * Reusable data formatting functions
12962  * @singleton
12963  */
12964 Roo.util.Format = function(){
12965     var trimRe = /^\s+|\s+$/g;
12966     return {
12967         /**
12968          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
12969          * @param {String} value The string to truncate
12970          * @param {Number} length The maximum length to allow before truncating
12971          * @return {String} The converted text
12972          */
12973         ellipsis : function(value, len){
12974             if(value && value.length > len){
12975                 return value.substr(0, len-3)+"...";
12976             }
12977             return value;
12978         },
12979
12980         /**
12981          * Checks a reference and converts it to empty string if it is undefined
12982          * @param {Mixed} value Reference to check
12983          * @return {Mixed} Empty string if converted, otherwise the original value
12984          */
12985         undef : function(value){
12986             return typeof value != "undefined" ? value : "";
12987         },
12988
12989         /**
12990          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
12991          * @param {String} value The string to encode
12992          * @return {String} The encoded text
12993          */
12994         htmlEncode : function(value){
12995             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
12996         },
12997
12998         /**
12999          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
13000          * @param {String} value The string to decode
13001          * @return {String} The decoded text
13002          */
13003         htmlDecode : function(value){
13004             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
13005         },
13006
13007         /**
13008          * Trims any whitespace from either side of a string
13009          * @param {String} value The text to trim
13010          * @return {String} The trimmed text
13011          */
13012         trim : function(value){
13013             return String(value).replace(trimRe, "");
13014         },
13015
13016         /**
13017          * Returns a substring from within an original string
13018          * @param {String} value The original text
13019          * @param {Number} start The start index of the substring
13020          * @param {Number} length The length of the substring
13021          * @return {String} The substring
13022          */
13023         substr : function(value, start, length){
13024             return String(value).substr(start, length);
13025         },
13026
13027         /**
13028          * Converts a string to all lower case letters
13029          * @param {String} value The text to convert
13030          * @return {String} The converted text
13031          */
13032         lowercase : function(value){
13033             return String(value).toLowerCase();
13034         },
13035
13036         /**
13037          * Converts a string to all upper case letters
13038          * @param {String} value The text to convert
13039          * @return {String} The converted text
13040          */
13041         uppercase : function(value){
13042             return String(value).toUpperCase();
13043         },
13044
13045         /**
13046          * Converts the first character only of a string to upper case
13047          * @param {String} value The text to convert
13048          * @return {String} The converted text
13049          */
13050         capitalize : function(value){
13051             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13052         },
13053
13054         // private
13055         call : function(value, fn){
13056             if(arguments.length > 2){
13057                 var args = Array.prototype.slice.call(arguments, 2);
13058                 args.unshift(value);
13059                  
13060                 return /** eval:var:value */  eval(fn).apply(window, args);
13061             }else{
13062                 /** eval:var:value */
13063                 return /** eval:var:value */ eval(fn).call(window, value);
13064             }
13065         },
13066
13067        
13068         /**
13069          * safer version of Math.toFixed..??/
13070          * @param {Number/String} value The numeric value to format
13071          * @param {Number/String} value Decimal places 
13072          * @return {String} The formatted currency string
13073          */
13074         toFixed : function(v, n)
13075         {
13076             // why not use to fixed - precision is buggered???
13077             if (!n) {
13078                 return Math.round(v-0);
13079             }
13080             var fact = Math.pow(10,n+1);
13081             v = (Math.round((v-0)*fact))/fact;
13082             var z = (''+fact).substring(2);
13083             if (v == Math.floor(v)) {
13084                 return Math.floor(v) + '.' + z;
13085             }
13086             
13087             // now just padd decimals..
13088             var ps = String(v).split('.');
13089             var fd = (ps[1] + z);
13090             var r = fd.substring(0,n); 
13091             var rm = fd.substring(n); 
13092             if (rm < 5) {
13093                 return ps[0] + '.' + r;
13094             }
13095             r*=1; // turn it into a number;
13096             r++;
13097             if (String(r).length != n) {
13098                 ps[0]*=1;
13099                 ps[0]++;
13100                 r = String(r).substring(1); // chop the end off.
13101             }
13102             
13103             return ps[0] + '.' + r;
13104              
13105         },
13106         
13107         /**
13108          * Format a number as US currency
13109          * @param {Number/String} value The numeric value to format
13110          * @return {String} The formatted currency string
13111          */
13112         usMoney : function(v){
13113             v = (Math.round((v-0)*100))/100;
13114             v = (v == Math.floor(v)) ? v + ".00" : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13115             v = String(v);
13116             var ps = v.split('.');
13117             var whole = ps[0];
13118             var sub = ps[1] ? '.'+ ps[1] : '.00';
13119             var r = /(\d+)(\d{3})/;
13120             while (r.test(whole)) {
13121                 whole = whole.replace(r, '$1' + ',' + '$2');
13122             }
13123             return "$" + whole + sub ;
13124         },
13125         
13126         /**
13127          * Parse a value into a formatted date using the specified format pattern.
13128          * @param {Mixed} value The value to format
13129          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13130          * @return {String} The formatted date string
13131          */
13132         date : function(v, format){
13133             if(!v){
13134                 return "";
13135             }
13136             if(!(v instanceof Date)){
13137                 v = new Date(Date.parse(v));
13138             }
13139             return v.dateFormat(format || "m/d/Y");
13140         },
13141
13142         /**
13143          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13144          * @param {String} format Any valid date format string
13145          * @return {Function} The date formatting function
13146          */
13147         dateRenderer : function(format){
13148             return function(v){
13149                 return Roo.util.Format.date(v, format);  
13150             };
13151         },
13152
13153         // private
13154         stripTagsRE : /<\/?[^>]+>/gi,
13155         
13156         /**
13157          * Strips all HTML tags
13158          * @param {Mixed} value The text from which to strip tags
13159          * @return {String} The stripped text
13160          */
13161         stripTags : function(v){
13162             return !v ? v : String(v).replace(this.stripTagsRE, "");
13163         }
13164     };
13165 }();/*
13166  * Based on:
13167  * Ext JS Library 1.1.1
13168  * Copyright(c) 2006-2007, Ext JS, LLC.
13169  *
13170  * Originally Released Under LGPL - original licence link has changed is not relivant.
13171  *
13172  * Fork - LGPL
13173  * <script type="text/javascript">
13174  */
13175
13176
13177  
13178
13179 /**
13180  * @class Roo.MasterTemplate
13181  * @extends Roo.Template
13182  * Provides a template that can have child templates. The syntax is:
13183 <pre><code>
13184 var t = new Roo.MasterTemplate(
13185         '&lt;select name="{name}"&gt;',
13186                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13187         '&lt;/select&gt;'
13188 );
13189 t.add('options', {value: 'foo', text: 'bar'});
13190 // or you can add multiple child elements in one shot
13191 t.addAll('options', [
13192     {value: 'foo', text: 'bar'},
13193     {value: 'foo2', text: 'bar2'},
13194     {value: 'foo3', text: 'bar3'}
13195 ]);
13196 // then append, applying the master template values
13197 t.append('my-form', {name: 'my-select'});
13198 </code></pre>
13199 * A name attribute for the child template is not required if you have only one child
13200 * template or you want to refer to them by index.
13201  */
13202 Roo.MasterTemplate = function(){
13203     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13204     this.originalHtml = this.html;
13205     var st = {};
13206     var m, re = this.subTemplateRe;
13207     re.lastIndex = 0;
13208     var subIndex = 0;
13209     while(m = re.exec(this.html)){
13210         var name = m[1], content = m[2];
13211         st[subIndex] = {
13212             name: name,
13213             index: subIndex,
13214             buffer: [],
13215             tpl : new Roo.Template(content)
13216         };
13217         if(name){
13218             st[name] = st[subIndex];
13219         }
13220         st[subIndex].tpl.compile();
13221         st[subIndex].tpl.call = this.call.createDelegate(this);
13222         subIndex++;
13223     }
13224     this.subCount = subIndex;
13225     this.subs = st;
13226 };
13227 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13228     /**
13229     * The regular expression used to match sub templates
13230     * @type RegExp
13231     * @property
13232     */
13233     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13234
13235     /**
13236      * Applies the passed values to a child template.
13237      * @param {String/Number} name (optional) The name or index of the child template
13238      * @param {Array/Object} values The values to be applied to the template
13239      * @return {MasterTemplate} this
13240      */
13241      add : function(name, values){
13242         if(arguments.length == 1){
13243             values = arguments[0];
13244             name = 0;
13245         }
13246         var s = this.subs[name];
13247         s.buffer[s.buffer.length] = s.tpl.apply(values);
13248         return this;
13249     },
13250
13251     /**
13252      * Applies all the passed values to a child template.
13253      * @param {String/Number} name (optional) The name or index of the child template
13254      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13255      * @param {Boolean} reset (optional) True to reset the template first
13256      * @return {MasterTemplate} this
13257      */
13258     fill : function(name, values, reset){
13259         var a = arguments;
13260         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13261             values = a[0];
13262             name = 0;
13263             reset = a[1];
13264         }
13265         if(reset){
13266             this.reset();
13267         }
13268         for(var i = 0, len = values.length; i < len; i++){
13269             this.add(name, values[i]);
13270         }
13271         return this;
13272     },
13273
13274     /**
13275      * Resets the template for reuse
13276      * @return {MasterTemplate} this
13277      */
13278      reset : function(){
13279         var s = this.subs;
13280         for(var i = 0; i < this.subCount; i++){
13281             s[i].buffer = [];
13282         }
13283         return this;
13284     },
13285
13286     applyTemplate : function(values){
13287         var s = this.subs;
13288         var replaceIndex = -1;
13289         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13290             return s[++replaceIndex].buffer.join("");
13291         });
13292         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13293     },
13294
13295     apply : function(){
13296         return this.applyTemplate.apply(this, arguments);
13297     },
13298
13299     compile : function(){return this;}
13300 });
13301
13302 /**
13303  * Alias for fill().
13304  * @method
13305  */
13306 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13307  /**
13308  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13309  * var tpl = Roo.MasterTemplate.from('element-id');
13310  * @param {String/HTMLElement} el
13311  * @param {Object} config
13312  * @static
13313  */
13314 Roo.MasterTemplate.from = function(el, config){
13315     el = Roo.getDom(el);
13316     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13317 };/*
13318  * Based on:
13319  * Ext JS Library 1.1.1
13320  * Copyright(c) 2006-2007, Ext JS, LLC.
13321  *
13322  * Originally Released Under LGPL - original licence link has changed is not relivant.
13323  *
13324  * Fork - LGPL
13325  * <script type="text/javascript">
13326  */
13327
13328  
13329 /**
13330  * @class Roo.util.CSS
13331  * Utility class for manipulating CSS rules
13332  * @singleton
13333  */
13334 Roo.util.CSS = function(){
13335         var rules = null;
13336         var doc = document;
13337
13338     var camelRe = /(-[a-z])/gi;
13339     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13340
13341    return {
13342    /**
13343     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
13344     * tag and appended to the HEAD of the document.
13345     * @param {String|Object} cssText The text containing the css rules
13346     * @param {String} id An id to add to the stylesheet for later removal
13347     * @return {StyleSheet}
13348     */
13349     createStyleSheet : function(cssText, id){
13350         var ss;
13351         var head = doc.getElementsByTagName("head")[0];
13352         var nrules = doc.createElement("style");
13353         nrules.setAttribute("type", "text/css");
13354         if(id){
13355             nrules.setAttribute("id", id);
13356         }
13357         if (typeof(cssText) != 'string') {
13358             // support object maps..
13359             // not sure if this a good idea.. 
13360             // perhaps it should be merged with the general css handling
13361             // and handle js style props.
13362             var cssTextNew = [];
13363             for(var n in cssText) {
13364                 var citems = [];
13365                 for(var k in cssText[n]) {
13366                     citems.push( k + ' : ' +cssText[n][k] + ';' );
13367                 }
13368                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
13369                 
13370             }
13371             cssText = cssTextNew.join("\n");
13372             
13373         }
13374        
13375        
13376        if(Roo.isIE){
13377            head.appendChild(nrules);
13378            ss = nrules.styleSheet;
13379            ss.cssText = cssText;
13380        }else{
13381            try{
13382                 nrules.appendChild(doc.createTextNode(cssText));
13383            }catch(e){
13384                nrules.cssText = cssText; 
13385            }
13386            head.appendChild(nrules);
13387            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
13388        }
13389        this.cacheStyleSheet(ss);
13390        return ss;
13391    },
13392
13393    /**
13394     * Removes a style or link tag by id
13395     * @param {String} id The id of the tag
13396     */
13397    removeStyleSheet : function(id){
13398        var existing = doc.getElementById(id);
13399        if(existing){
13400            existing.parentNode.removeChild(existing);
13401        }
13402    },
13403
13404    /**
13405     * Dynamically swaps an existing stylesheet reference for a new one
13406     * @param {String} id The id of an existing link tag to remove
13407     * @param {String} url The href of the new stylesheet to include
13408     */
13409    swapStyleSheet : function(id, url){
13410        this.removeStyleSheet(id);
13411        var ss = doc.createElement("link");
13412        ss.setAttribute("rel", "stylesheet");
13413        ss.setAttribute("type", "text/css");
13414        ss.setAttribute("id", id);
13415        ss.setAttribute("href", url);
13416        doc.getElementsByTagName("head")[0].appendChild(ss);
13417    },
13418    
13419    /**
13420     * Refresh the rule cache if you have dynamically added stylesheets
13421     * @return {Object} An object (hash) of rules indexed by selector
13422     */
13423    refreshCache : function(){
13424        return this.getRules(true);
13425    },
13426
13427    // private
13428    cacheStyleSheet : function(stylesheet){
13429        if(!rules){
13430            rules = {};
13431        }
13432        try{// try catch for cross domain access issue
13433            var ssRules = stylesheet.cssRules || stylesheet.rules;
13434            for(var j = ssRules.length-1; j >= 0; --j){
13435                rules[ssRules[j].selectorText] = ssRules[j];
13436            }
13437        }catch(e){}
13438    },
13439    
13440    /**
13441     * Gets all css rules for the document
13442     * @param {Boolean} refreshCache true to refresh the internal cache
13443     * @return {Object} An object (hash) of rules indexed by selector
13444     */
13445    getRules : function(refreshCache){
13446                 if(rules == null || refreshCache){
13447                         rules = {};
13448                         var ds = doc.styleSheets;
13449                         for(var i =0, len = ds.length; i < len; i++){
13450                             try{
13451                         this.cacheStyleSheet(ds[i]);
13452                     }catch(e){} 
13453                 }
13454                 }
13455                 return rules;
13456         },
13457         
13458         /**
13459     * Gets an an individual CSS rule by selector(s)
13460     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
13461     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
13462     * @return {CSSRule} The CSS rule or null if one is not found
13463     */
13464    getRule : function(selector, refreshCache){
13465                 var rs = this.getRules(refreshCache);
13466                 if(!(selector instanceof Array)){
13467                     return rs[selector];
13468                 }
13469                 for(var i = 0; i < selector.length; i++){
13470                         if(rs[selector[i]]){
13471                                 return rs[selector[i]];
13472                         }
13473                 }
13474                 return null;
13475         },
13476         
13477         
13478         /**
13479     * Updates a rule property
13480     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
13481     * @param {String} property The css property
13482     * @param {String} value The new value for the property
13483     * @return {Boolean} true If a rule was found and updated
13484     */
13485    updateRule : function(selector, property, value){
13486                 if(!(selector instanceof Array)){
13487                         var rule = this.getRule(selector);
13488                         if(rule){
13489                                 rule.style[property.replace(camelRe, camelFn)] = value;
13490                                 return true;
13491                         }
13492                 }else{
13493                         for(var i = 0; i < selector.length; i++){
13494                                 if(this.updateRule(selector[i], property, value)){
13495                                         return true;
13496                                 }
13497                         }
13498                 }
13499                 return false;
13500         }
13501    };   
13502 }();/*
13503  * Based on:
13504  * Ext JS Library 1.1.1
13505  * Copyright(c) 2006-2007, Ext JS, LLC.
13506  *
13507  * Originally Released Under LGPL - original licence link has changed is not relivant.
13508  *
13509  * Fork - LGPL
13510  * <script type="text/javascript">
13511  */
13512
13513  
13514
13515 /**
13516  * @class Roo.util.ClickRepeater
13517  * @extends Roo.util.Observable
13518  * 
13519  * A wrapper class which can be applied to any element. Fires a "click" event while the
13520  * mouse is pressed. The interval between firings may be specified in the config but
13521  * defaults to 10 milliseconds.
13522  * 
13523  * Optionally, a CSS class may be applied to the element during the time it is pressed.
13524  * 
13525  * @cfg {String/HTMLElement/Element} el The element to act as a button.
13526  * @cfg {Number} delay The initial delay before the repeating event begins firing.
13527  * Similar to an autorepeat key delay.
13528  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
13529  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
13530  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
13531  *           "interval" and "delay" are ignored. "immediate" is honored.
13532  * @cfg {Boolean} preventDefault True to prevent the default click event
13533  * @cfg {Boolean} stopDefault True to stop the default click event
13534  * 
13535  * @history
13536  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
13537  *     2007-02-02 jvs Renamed to ClickRepeater
13538  *   2007-02-03 jvs Modifications for FF Mac and Safari 
13539  *
13540  *  @constructor
13541  * @param {String/HTMLElement/Element} el The element to listen on
13542  * @param {Object} config
13543  **/
13544 Roo.util.ClickRepeater = function(el, config)
13545 {
13546     this.el = Roo.get(el);
13547     this.el.unselectable();
13548
13549     Roo.apply(this, config);
13550
13551     this.addEvents({
13552     /**
13553      * @event mousedown
13554      * Fires when the mouse button is depressed.
13555      * @param {Roo.util.ClickRepeater} this
13556      */
13557         "mousedown" : true,
13558     /**
13559      * @event click
13560      * Fires on a specified interval during the time the element is pressed.
13561      * @param {Roo.util.ClickRepeater} this
13562      */
13563         "click" : true,
13564     /**
13565      * @event mouseup
13566      * Fires when the mouse key is released.
13567      * @param {Roo.util.ClickRepeater} this
13568      */
13569         "mouseup" : true
13570     });
13571
13572     this.el.on("mousedown", this.handleMouseDown, this);
13573     if(this.preventDefault || this.stopDefault){
13574         this.el.on("click", function(e){
13575             if(this.preventDefault){
13576                 e.preventDefault();
13577             }
13578             if(this.stopDefault){
13579                 e.stopEvent();
13580             }
13581         }, this);
13582     }
13583
13584     // allow inline handler
13585     if(this.handler){
13586         this.on("click", this.handler,  this.scope || this);
13587     }
13588
13589     Roo.util.ClickRepeater.superclass.constructor.call(this);
13590 };
13591
13592 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
13593     interval : 20,
13594     delay: 250,
13595     preventDefault : true,
13596     stopDefault : false,
13597     timer : 0,
13598
13599     // private
13600     handleMouseDown : function(){
13601         clearTimeout(this.timer);
13602         this.el.blur();
13603         if(this.pressClass){
13604             this.el.addClass(this.pressClass);
13605         }
13606         this.mousedownTime = new Date();
13607
13608         Roo.get(document).on("mouseup", this.handleMouseUp, this);
13609         this.el.on("mouseout", this.handleMouseOut, this);
13610
13611         this.fireEvent("mousedown", this);
13612         this.fireEvent("click", this);
13613         
13614         this.timer = this.click.defer(this.delay || this.interval, this);
13615     },
13616
13617     // private
13618     click : function(){
13619         this.fireEvent("click", this);
13620         this.timer = this.click.defer(this.getInterval(), this);
13621     },
13622
13623     // private
13624     getInterval: function(){
13625         if(!this.accelerate){
13626             return this.interval;
13627         }
13628         var pressTime = this.mousedownTime.getElapsed();
13629         if(pressTime < 500){
13630             return 400;
13631         }else if(pressTime < 1700){
13632             return 320;
13633         }else if(pressTime < 2600){
13634             return 250;
13635         }else if(pressTime < 3500){
13636             return 180;
13637         }else if(pressTime < 4400){
13638             return 140;
13639         }else if(pressTime < 5300){
13640             return 80;
13641         }else if(pressTime < 6200){
13642             return 50;
13643         }else{
13644             return 10;
13645         }
13646     },
13647
13648     // private
13649     handleMouseOut : function(){
13650         clearTimeout(this.timer);
13651         if(this.pressClass){
13652             this.el.removeClass(this.pressClass);
13653         }
13654         this.el.on("mouseover", this.handleMouseReturn, this);
13655     },
13656
13657     // private
13658     handleMouseReturn : function(){
13659         this.el.un("mouseover", this.handleMouseReturn);
13660         if(this.pressClass){
13661             this.el.addClass(this.pressClass);
13662         }
13663         this.click();
13664     },
13665
13666     // private
13667     handleMouseUp : function(){
13668         clearTimeout(this.timer);
13669         this.el.un("mouseover", this.handleMouseReturn);
13670         this.el.un("mouseout", this.handleMouseOut);
13671         Roo.get(document).un("mouseup", this.handleMouseUp);
13672         this.el.removeClass(this.pressClass);
13673         this.fireEvent("mouseup", this);
13674     }
13675 });/*
13676  * Based on:
13677  * Ext JS Library 1.1.1
13678  * Copyright(c) 2006-2007, Ext JS, LLC.
13679  *
13680  * Originally Released Under LGPL - original licence link has changed is not relivant.
13681  *
13682  * Fork - LGPL
13683  * <script type="text/javascript">
13684  */
13685
13686  
13687 /**
13688  * @class Roo.KeyNav
13689  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
13690  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
13691  * way to implement custom navigation schemes for any UI component.</p>
13692  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
13693  * pageUp, pageDown, del, home, end.  Usage:</p>
13694  <pre><code>
13695 var nav = new Roo.KeyNav("my-element", {
13696     "left" : function(e){
13697         this.moveLeft(e.ctrlKey);
13698     },
13699     "right" : function(e){
13700         this.moveRight(e.ctrlKey);
13701     },
13702     "enter" : function(e){
13703         this.save();
13704     },
13705     scope : this
13706 });
13707 </code></pre>
13708  * @constructor
13709  * @param {String/HTMLElement/Roo.Element} el The element to bind to
13710  * @param {Object} config The config
13711  */
13712 Roo.KeyNav = function(el, config){
13713     this.el = Roo.get(el);
13714     Roo.apply(this, config);
13715     if(!this.disabled){
13716         this.disabled = true;
13717         this.enable();
13718     }
13719 };
13720
13721 Roo.KeyNav.prototype = {
13722     /**
13723      * @cfg {Boolean} disabled
13724      * True to disable this KeyNav instance (defaults to false)
13725      */
13726     disabled : false,
13727     /**
13728      * @cfg {String} defaultEventAction
13729      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
13730      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
13731      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
13732      */
13733     defaultEventAction: "stopEvent",
13734     /**
13735      * @cfg {Boolean} forceKeyDown
13736      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
13737      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
13738      * handle keydown instead of keypress.
13739      */
13740     forceKeyDown : false,
13741
13742     // private
13743     prepareEvent : function(e){
13744         var k = e.getKey();
13745         var h = this.keyToHandler[k];
13746         //if(h && this[h]){
13747         //    e.stopPropagation();
13748         //}
13749         if(Roo.isSafari && h && k >= 37 && k <= 40){
13750             e.stopEvent();
13751         }
13752     },
13753
13754     // private
13755     relay : function(e){
13756         var k = e.getKey();
13757         var h = this.keyToHandler[k];
13758         if(h && this[h]){
13759             if(this.doRelay(e, this[h], h) !== true){
13760                 e[this.defaultEventAction]();
13761             }
13762         }
13763     },
13764
13765     // private
13766     doRelay : function(e, h, hname){
13767         return h.call(this.scope || this, e);
13768     },
13769
13770     // possible handlers
13771     enter : false,
13772     left : false,
13773     right : false,
13774     up : false,
13775     down : false,
13776     tab : false,
13777     esc : false,
13778     pageUp : false,
13779     pageDown : false,
13780     del : false,
13781     home : false,
13782     end : false,
13783
13784     // quick lookup hash
13785     keyToHandler : {
13786         37 : "left",
13787         39 : "right",
13788         38 : "up",
13789         40 : "down",
13790         33 : "pageUp",
13791         34 : "pageDown",
13792         46 : "del",
13793         36 : "home",
13794         35 : "end",
13795         13 : "enter",
13796         27 : "esc",
13797         9  : "tab"
13798     },
13799
13800         /**
13801          * Enable this KeyNav
13802          */
13803         enable: function(){
13804                 if(this.disabled){
13805             // ie won't do special keys on keypress, no one else will repeat keys with keydown
13806             // the EventObject will normalize Safari automatically
13807             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
13808                 this.el.on("keydown", this.relay,  this);
13809             }else{
13810                 this.el.on("keydown", this.prepareEvent,  this);
13811                 this.el.on("keypress", this.relay,  this);
13812             }
13813                     this.disabled = false;
13814                 }
13815         },
13816
13817         /**
13818          * Disable this KeyNav
13819          */
13820         disable: function(){
13821                 if(!this.disabled){
13822                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
13823                 this.el.un("keydown", this.relay);
13824             }else{
13825                 this.el.un("keydown", this.prepareEvent);
13826                 this.el.un("keypress", this.relay);
13827             }
13828                     this.disabled = true;
13829                 }
13830         }
13831 };/*
13832  * Based on:
13833  * Ext JS Library 1.1.1
13834  * Copyright(c) 2006-2007, Ext JS, LLC.
13835  *
13836  * Originally Released Under LGPL - original licence link has changed is not relivant.
13837  *
13838  * Fork - LGPL
13839  * <script type="text/javascript">
13840  */
13841
13842  
13843 /**
13844  * @class Roo.KeyMap
13845  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
13846  * The constructor accepts the same config object as defined by {@link #addBinding}.
13847  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
13848  * combination it will call the function with this signature (if the match is a multi-key
13849  * combination the callback will still be called only once): (String key, Roo.EventObject e)
13850  * A KeyMap can also handle a string representation of keys.<br />
13851  * Usage:
13852  <pre><code>
13853 // map one key by key code
13854 var map = new Roo.KeyMap("my-element", {
13855     key: 13, // or Roo.EventObject.ENTER
13856     fn: myHandler,
13857     scope: myObject
13858 });
13859
13860 // map multiple keys to one action by string
13861 var map = new Roo.KeyMap("my-element", {
13862     key: "a\r\n\t",
13863     fn: myHandler,
13864     scope: myObject
13865 });
13866
13867 // map multiple keys to multiple actions by strings and array of codes
13868 var map = new Roo.KeyMap("my-element", [
13869     {
13870         key: [10,13],
13871         fn: function(){ alert("Return was pressed"); }
13872     }, {
13873         key: "abc",
13874         fn: function(){ alert('a, b or c was pressed'); }
13875     }, {
13876         key: "\t",
13877         ctrl:true,
13878         shift:true,
13879         fn: function(){ alert('Control + shift + tab was pressed.'); }
13880     }
13881 ]);
13882 </code></pre>
13883  * <b>Note: A KeyMap starts enabled</b>
13884  * @constructor
13885  * @param {String/HTMLElement/Roo.Element} el The element to bind to
13886  * @param {Object} config The config (see {@link #addBinding})
13887  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
13888  */
13889 Roo.KeyMap = function(el, config, eventName){
13890     this.el  = Roo.get(el);
13891     this.eventName = eventName || "keydown";
13892     this.bindings = [];
13893     if(config){
13894         this.addBinding(config);
13895     }
13896     this.enable();
13897 };
13898
13899 Roo.KeyMap.prototype = {
13900     /**
13901      * True to stop the event from bubbling and prevent the default browser action if the
13902      * key was handled by the KeyMap (defaults to false)
13903      * @type Boolean
13904      */
13905     stopEvent : false,
13906
13907     /**
13908      * Add a new binding to this KeyMap. The following config object properties are supported:
13909      * <pre>
13910 Property    Type             Description
13911 ----------  ---------------  ----------------------------------------------------------------------
13912 key         String/Array     A single keycode or an array of keycodes to handle
13913 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
13914 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
13915 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
13916 fn          Function         The function to call when KeyMap finds the expected key combination
13917 scope       Object           The scope of the callback function
13918 </pre>
13919      *
13920      * Usage:
13921      * <pre><code>
13922 // Create a KeyMap
13923 var map = new Roo.KeyMap(document, {
13924     key: Roo.EventObject.ENTER,
13925     fn: handleKey,
13926     scope: this
13927 });
13928
13929 //Add a new binding to the existing KeyMap later
13930 map.addBinding({
13931     key: 'abc',
13932     shift: true,
13933     fn: handleKey,
13934     scope: this
13935 });
13936 </code></pre>
13937      * @param {Object/Array} config A single KeyMap config or an array of configs
13938      */
13939         addBinding : function(config){
13940         if(config instanceof Array){
13941             for(var i = 0, len = config.length; i < len; i++){
13942                 this.addBinding(config[i]);
13943             }
13944             return;
13945         }
13946         var keyCode = config.key,
13947             shift = config.shift, 
13948             ctrl = config.ctrl, 
13949             alt = config.alt,
13950             fn = config.fn,
13951             scope = config.scope;
13952         if(typeof keyCode == "string"){
13953             var ks = [];
13954             var keyString = keyCode.toUpperCase();
13955             for(var j = 0, len = keyString.length; j < len; j++){
13956                 ks.push(keyString.charCodeAt(j));
13957             }
13958             keyCode = ks;
13959         }
13960         var keyArray = keyCode instanceof Array;
13961         var handler = function(e){
13962             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
13963                 var k = e.getKey();
13964                 if(keyArray){
13965                     for(var i = 0, len = keyCode.length; i < len; i++){
13966                         if(keyCode[i] == k){
13967                           if(this.stopEvent){
13968                               e.stopEvent();
13969                           }
13970                           fn.call(scope || window, k, e);
13971                           return;
13972                         }
13973                     }
13974                 }else{
13975                     if(k == keyCode){
13976                         if(this.stopEvent){
13977                            e.stopEvent();
13978                         }
13979                         fn.call(scope || window, k, e);
13980                     }
13981                 }
13982             }
13983         };
13984         this.bindings.push(handler);  
13985         },
13986
13987     /**
13988      * Shorthand for adding a single key listener
13989      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
13990      * following options:
13991      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
13992      * @param {Function} fn The function to call
13993      * @param {Object} scope (optional) The scope of the function
13994      */
13995     on : function(key, fn, scope){
13996         var keyCode, shift, ctrl, alt;
13997         if(typeof key == "object" && !(key instanceof Array)){
13998             keyCode = key.key;
13999             shift = key.shift;
14000             ctrl = key.ctrl;
14001             alt = key.alt;
14002         }else{
14003             keyCode = key;
14004         }
14005         this.addBinding({
14006             key: keyCode,
14007             shift: shift,
14008             ctrl: ctrl,
14009             alt: alt,
14010             fn: fn,
14011             scope: scope
14012         })
14013     },
14014
14015     // private
14016     handleKeyDown : function(e){
14017             if(this.enabled){ //just in case
14018             var b = this.bindings;
14019             for(var i = 0, len = b.length; i < len; i++){
14020                 b[i].call(this, e);
14021             }
14022             }
14023         },
14024         
14025         /**
14026          * Returns true if this KeyMap is enabled
14027          * @return {Boolean} 
14028          */
14029         isEnabled : function(){
14030             return this.enabled;  
14031         },
14032         
14033         /**
14034          * Enables this KeyMap
14035          */
14036         enable: function(){
14037                 if(!this.enabled){
14038                     this.el.on(this.eventName, this.handleKeyDown, this);
14039                     this.enabled = true;
14040                 }
14041         },
14042
14043         /**
14044          * Disable this KeyMap
14045          */
14046         disable: function(){
14047                 if(this.enabled){
14048                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14049                     this.enabled = false;
14050                 }
14051         }
14052 };/*
14053  * Based on:
14054  * Ext JS Library 1.1.1
14055  * Copyright(c) 2006-2007, Ext JS, LLC.
14056  *
14057  * Originally Released Under LGPL - original licence link has changed is not relivant.
14058  *
14059  * Fork - LGPL
14060  * <script type="text/javascript">
14061  */
14062
14063  
14064 /**
14065  * @class Roo.util.TextMetrics
14066  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14067  * wide, in pixels, a given block of text will be.
14068  * @singleton
14069  */
14070 Roo.util.TextMetrics = function(){
14071     var shared;
14072     return {
14073         /**
14074          * Measures the size of the specified text
14075          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14076          * that can affect the size of the rendered text
14077          * @param {String} text The text to measure
14078          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14079          * in order to accurately measure the text height
14080          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14081          */
14082         measure : function(el, text, fixedWidth){
14083             if(!shared){
14084                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14085             }
14086             shared.bind(el);
14087             shared.setFixedWidth(fixedWidth || 'auto');
14088             return shared.getSize(text);
14089         },
14090
14091         /**
14092          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14093          * the overhead of multiple calls to initialize the style properties on each measurement.
14094          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14095          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14096          * in order to accurately measure the text height
14097          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14098          */
14099         createInstance : function(el, fixedWidth){
14100             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14101         }
14102     };
14103 }();
14104
14105  
14106
14107 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14108     var ml = new Roo.Element(document.createElement('div'));
14109     document.body.appendChild(ml.dom);
14110     ml.position('absolute');
14111     ml.setLeftTop(-1000, -1000);
14112     ml.hide();
14113
14114     if(fixedWidth){
14115         ml.setWidth(fixedWidth);
14116     }
14117      
14118     var instance = {
14119         /**
14120          * Returns the size of the specified text based on the internal element's style and width properties
14121          * @memberOf Roo.util.TextMetrics.Instance#
14122          * @param {String} text The text to measure
14123          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14124          */
14125         getSize : function(text){
14126             ml.update(text);
14127             var s = ml.getSize();
14128             ml.update('');
14129             return s;
14130         },
14131
14132         /**
14133          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14134          * that can affect the size of the rendered text
14135          * @memberOf Roo.util.TextMetrics.Instance#
14136          * @param {String/HTMLElement} el The element, dom node or id
14137          */
14138         bind : function(el){
14139             ml.setStyle(
14140                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14141             );
14142         },
14143
14144         /**
14145          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14146          * to set a fixed width in order to accurately measure the text height.
14147          * @memberOf Roo.util.TextMetrics.Instance#
14148          * @param {Number} width The width to set on the element
14149          */
14150         setFixedWidth : function(width){
14151             ml.setWidth(width);
14152         },
14153
14154         /**
14155          * Returns the measured width of the specified text
14156          * @memberOf Roo.util.TextMetrics.Instance#
14157          * @param {String} text The text to measure
14158          * @return {Number} width The width in pixels
14159          */
14160         getWidth : function(text){
14161             ml.dom.style.width = 'auto';
14162             return this.getSize(text).width;
14163         },
14164
14165         /**
14166          * Returns the measured height of the specified text.  For multiline text, be sure to call
14167          * {@link #setFixedWidth} if necessary.
14168          * @memberOf Roo.util.TextMetrics.Instance#
14169          * @param {String} text The text to measure
14170          * @return {Number} height The height in pixels
14171          */
14172         getHeight : function(text){
14173             return this.getSize(text).height;
14174         }
14175     };
14176
14177     instance.bind(bindTo);
14178
14179     return instance;
14180 };
14181
14182 // backwards compat
14183 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14184  * Based on:
14185  * Ext JS Library 1.1.1
14186  * Copyright(c) 2006-2007, Ext JS, LLC.
14187  *
14188  * Originally Released Under LGPL - original licence link has changed is not relivant.
14189  *
14190  * Fork - LGPL
14191  * <script type="text/javascript">
14192  */
14193
14194 /**
14195  * @class Roo.state.Provider
14196  * Abstract base class for state provider implementations. This class provides methods
14197  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14198  * Provider interface.
14199  */
14200 Roo.state.Provider = function(){
14201     /**
14202      * @event statechange
14203      * Fires when a state change occurs.
14204      * @param {Provider} this This state provider
14205      * @param {String} key The state key which was changed
14206      * @param {String} value The encoded value for the state
14207      */
14208     this.addEvents({
14209         "statechange": true
14210     });
14211     this.state = {};
14212     Roo.state.Provider.superclass.constructor.call(this);
14213 };
14214 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14215     /**
14216      * Returns the current value for a key
14217      * @param {String} name The key name
14218      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14219      * @return {Mixed} The state data
14220      */
14221     get : function(name, defaultValue){
14222         return typeof this.state[name] == "undefined" ?
14223             defaultValue : this.state[name];
14224     },
14225     
14226     /**
14227      * Clears a value from the state
14228      * @param {String} name The key name
14229      */
14230     clear : function(name){
14231         delete this.state[name];
14232         this.fireEvent("statechange", this, name, null);
14233     },
14234     
14235     /**
14236      * Sets the value for a key
14237      * @param {String} name The key name
14238      * @param {Mixed} value The value to set
14239      */
14240     set : function(name, value){
14241         this.state[name] = value;
14242         this.fireEvent("statechange", this, name, value);
14243     },
14244     
14245     /**
14246      * Decodes a string previously encoded with {@link #encodeValue}.
14247      * @param {String} value The value to decode
14248      * @return {Mixed} The decoded value
14249      */
14250     decodeValue : function(cookie){
14251         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14252         var matches = re.exec(unescape(cookie));
14253         if(!matches || !matches[1]) return; // non state cookie
14254         var type = matches[1];
14255         var v = matches[2];
14256         switch(type){
14257             case "n":
14258                 return parseFloat(v);
14259             case "d":
14260                 return new Date(Date.parse(v));
14261             case "b":
14262                 return (v == "1");
14263             case "a":
14264                 var all = [];
14265                 var values = v.split("^");
14266                 for(var i = 0, len = values.length; i < len; i++){
14267                     all.push(this.decodeValue(values[i]));
14268                 }
14269                 return all;
14270            case "o":
14271                 var all = {};
14272                 var values = v.split("^");
14273                 for(var i = 0, len = values.length; i < len; i++){
14274                     var kv = values[i].split("=");
14275                     all[kv[0]] = this.decodeValue(kv[1]);
14276                 }
14277                 return all;
14278            default:
14279                 return v;
14280         }
14281     },
14282     
14283     /**
14284      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14285      * @param {Mixed} value The value to encode
14286      * @return {String} The encoded value
14287      */
14288     encodeValue : function(v){
14289         var enc;
14290         if(typeof v == "number"){
14291             enc = "n:" + v;
14292         }else if(typeof v == "boolean"){
14293             enc = "b:" + (v ? "1" : "0");
14294         }else if(v instanceof Date){
14295             enc = "d:" + v.toGMTString();
14296         }else if(v instanceof Array){
14297             var flat = "";
14298             for(var i = 0, len = v.length; i < len; i++){
14299                 flat += this.encodeValue(v[i]);
14300                 if(i != len-1) flat += "^";
14301             }
14302             enc = "a:" + flat;
14303         }else if(typeof v == "object"){
14304             var flat = "";
14305             for(var key in v){
14306                 if(typeof v[key] != "function"){
14307                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14308                 }
14309             }
14310             enc = "o:" + flat.substring(0, flat.length-1);
14311         }else{
14312             enc = "s:" + v;
14313         }
14314         return escape(enc);        
14315     }
14316 });
14317
14318 /*
14319  * Based on:
14320  * Ext JS Library 1.1.1
14321  * Copyright(c) 2006-2007, Ext JS, LLC.
14322  *
14323  * Originally Released Under LGPL - original licence link has changed is not relivant.
14324  *
14325  * Fork - LGPL
14326  * <script type="text/javascript">
14327  */
14328 /**
14329  * @class Roo.state.Manager
14330  * This is the global state manager. By default all components that are "state aware" check this class
14331  * for state information if you don't pass them a custom state provider. In order for this class
14332  * to be useful, it must be initialized with a provider when your application initializes.
14333  <pre><code>
14334 // in your initialization function
14335 init : function(){
14336    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14337    ...
14338    // supposed you have a {@link Roo.BorderLayout}
14339    var layout = new Roo.BorderLayout(...);
14340    layout.restoreState();
14341    // or a {Roo.BasicDialog}
14342    var dialog = new Roo.BasicDialog(...);
14343    dialog.restoreState();
14344  </code></pre>
14345  * @singleton
14346  */
14347 Roo.state.Manager = function(){
14348     var provider = new Roo.state.Provider();
14349     
14350     return {
14351         /**
14352          * Configures the default state provider for your application
14353          * @param {Provider} stateProvider The state provider to set
14354          */
14355         setProvider : function(stateProvider){
14356             provider = stateProvider;
14357         },
14358         
14359         /**
14360          * Returns the current value for a key
14361          * @param {String} name The key name
14362          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
14363          * @return {Mixed} The state data
14364          */
14365         get : function(key, defaultValue){
14366             return provider.get(key, defaultValue);
14367         },
14368         
14369         /**
14370          * Sets the value for a key
14371          * @param {String} name The key name
14372          * @param {Mixed} value The state data
14373          */
14374          set : function(key, value){
14375             provider.set(key, value);
14376         },
14377         
14378         /**
14379          * Clears a value from the state
14380          * @param {String} name The key name
14381          */
14382         clear : function(key){
14383             provider.clear(key);
14384         },
14385         
14386         /**
14387          * Gets the currently configured state provider
14388          * @return {Provider} The state provider
14389          */
14390         getProvider : function(){
14391             return provider;
14392         }
14393     };
14394 }();
14395 /*
14396  * Based on:
14397  * Ext JS Library 1.1.1
14398  * Copyright(c) 2006-2007, Ext JS, LLC.
14399  *
14400  * Originally Released Under LGPL - original licence link has changed is not relivant.
14401  *
14402  * Fork - LGPL
14403  * <script type="text/javascript">
14404  */
14405 /**
14406  * @class Roo.state.CookieProvider
14407  * @extends Roo.state.Provider
14408  * The default Provider implementation which saves state via cookies.
14409  * <br />Usage:
14410  <pre><code>
14411    var cp = new Roo.state.CookieProvider({
14412        path: "/cgi-bin/",
14413        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
14414        domain: "roojs.com"
14415    })
14416    Roo.state.Manager.setProvider(cp);
14417  </code></pre>
14418  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
14419  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
14420  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
14421  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
14422  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
14423  * domain the page is running on including the 'www' like 'www.roojs.com')
14424  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
14425  * @constructor
14426  * Create a new CookieProvider
14427  * @param {Object} config The configuration object
14428  */
14429 Roo.state.CookieProvider = function(config){
14430     Roo.state.CookieProvider.superclass.constructor.call(this);
14431     this.path = "/";
14432     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
14433     this.domain = null;
14434     this.secure = false;
14435     Roo.apply(this, config);
14436     this.state = this.readCookies();
14437 };
14438
14439 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
14440     // private
14441     set : function(name, value){
14442         if(typeof value == "undefined" || value === null){
14443             this.clear(name);
14444             return;
14445         }
14446         this.setCookie(name, value);
14447         Roo.state.CookieProvider.superclass.set.call(this, name, value);
14448     },
14449
14450     // private
14451     clear : function(name){
14452         this.clearCookie(name);
14453         Roo.state.CookieProvider.superclass.clear.call(this, name);
14454     },
14455
14456     // private
14457     readCookies : function(){
14458         var cookies = {};
14459         var c = document.cookie + ";";
14460         var re = /\s?(.*?)=(.*?);/g;
14461         var matches;
14462         while((matches = re.exec(c)) != null){
14463             var name = matches[1];
14464             var value = matches[2];
14465             if(name && name.substring(0,3) == "ys-"){
14466                 cookies[name.substr(3)] = this.decodeValue(value);
14467             }
14468         }
14469         return cookies;
14470     },
14471
14472     // private
14473     setCookie : function(name, value){
14474         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
14475            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
14476            ((this.path == null) ? "" : ("; path=" + this.path)) +
14477            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14478            ((this.secure == true) ? "; secure" : "");
14479     },
14480
14481     // private
14482     clearCookie : function(name){
14483         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
14484            ((this.path == null) ? "" : ("; path=" + this.path)) +
14485            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14486            ((this.secure == true) ? "; secure" : "");
14487     }
14488 });/*
14489  * Based on:
14490  * Ext JS Library 1.1.1
14491  * Copyright(c) 2006-2007, Ext JS, LLC.
14492  *
14493  * Originally Released Under LGPL - original licence link has changed is not relivant.
14494  *
14495  * Fork - LGPL
14496  * <script type="text/javascript">
14497  */
14498
14499
14500
14501 /*
14502  * These classes are derivatives of the similarly named classes in the YUI Library.
14503  * The original license:
14504  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
14505  * Code licensed under the BSD License:
14506  * http://developer.yahoo.net/yui/license.txt
14507  */
14508
14509 (function() {
14510
14511 var Event=Roo.EventManager;
14512 var Dom=Roo.lib.Dom;
14513
14514 /**
14515  * @class Roo.dd.DragDrop
14516  * @extends Roo.util.Observable
14517  * Defines the interface and base operation of items that that can be
14518  * dragged or can be drop targets.  It was designed to be extended, overriding
14519  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
14520  * Up to three html elements can be associated with a DragDrop instance:
14521  * <ul>
14522  * <li>linked element: the element that is passed into the constructor.
14523  * This is the element which defines the boundaries for interaction with
14524  * other DragDrop objects.</li>
14525  * <li>handle element(s): The drag operation only occurs if the element that
14526  * was clicked matches a handle element.  By default this is the linked
14527  * element, but there are times that you will want only a portion of the
14528  * linked element to initiate the drag operation, and the setHandleElId()
14529  * method provides a way to define this.</li>
14530  * <li>drag element: this represents the element that would be moved along
14531  * with the cursor during a drag operation.  By default, this is the linked
14532  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
14533  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
14534  * </li>
14535  * </ul>
14536  * This class should not be instantiated until the onload event to ensure that
14537  * the associated elements are available.
14538  * The following would define a DragDrop obj that would interact with any
14539  * other DragDrop obj in the "group1" group:
14540  * <pre>
14541  *  dd = new Roo.dd.DragDrop("div1", "group1");
14542  * </pre>
14543  * Since none of the event handlers have been implemented, nothing would
14544  * actually happen if you were to run the code above.  Normally you would
14545  * override this class or one of the default implementations, but you can
14546  * also override the methods you want on an instance of the class...
14547  * <pre>
14548  *  dd.onDragDrop = function(e, id) {
14549  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
14550  *  }
14551  * </pre>
14552  * @constructor
14553  * @param {String} id of the element that is linked to this instance
14554  * @param {String} sGroup the group of related DragDrop objects
14555  * @param {object} config an object containing configurable attributes
14556  *                Valid properties for DragDrop:
14557  *                    padding, isTarget, maintainOffset, primaryButtonOnly
14558  */
14559 Roo.dd.DragDrop = function(id, sGroup, config) {
14560     if (id) {
14561         this.init(id, sGroup, config);
14562     }
14563     
14564 };
14565
14566 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
14567
14568     /**
14569      * The id of the element associated with this object.  This is what we
14570      * refer to as the "linked element" because the size and position of
14571      * this element is used to determine when the drag and drop objects have
14572      * interacted.
14573      * @property id
14574      * @type String
14575      */
14576     id: null,
14577
14578     /**
14579      * Configuration attributes passed into the constructor
14580      * @property config
14581      * @type object
14582      */
14583     config: null,
14584
14585     /**
14586      * The id of the element that will be dragged.  By default this is same
14587      * as the linked element , but could be changed to another element. Ex:
14588      * Roo.dd.DDProxy
14589      * @property dragElId
14590      * @type String
14591      * @private
14592      */
14593     dragElId: null,
14594
14595     /**
14596      * the id of the element that initiates the drag operation.  By default
14597      * this is the linked element, but could be changed to be a child of this
14598      * element.  This lets us do things like only starting the drag when the
14599      * header element within the linked html element is clicked.
14600      * @property handleElId
14601      * @type String
14602      * @private
14603      */
14604     handleElId: null,
14605
14606     /**
14607      * An associative array of HTML tags that will be ignored if clicked.
14608      * @property invalidHandleTypes
14609      * @type {string: string}
14610      */
14611     invalidHandleTypes: null,
14612
14613     /**
14614      * An associative array of ids for elements that will be ignored if clicked
14615      * @property invalidHandleIds
14616      * @type {string: string}
14617      */
14618     invalidHandleIds: null,
14619
14620     /**
14621      * An indexted array of css class names for elements that will be ignored
14622      * if clicked.
14623      * @property invalidHandleClasses
14624      * @type string[]
14625      */
14626     invalidHandleClasses: null,
14627
14628     /**
14629      * The linked element's absolute X position at the time the drag was
14630      * started
14631      * @property startPageX
14632      * @type int
14633      * @private
14634      */
14635     startPageX: 0,
14636
14637     /**
14638      * The linked element's absolute X position at the time the drag was
14639      * started
14640      * @property startPageY
14641      * @type int
14642      * @private
14643      */
14644     startPageY: 0,
14645
14646     /**
14647      * The group defines a logical collection of DragDrop objects that are
14648      * related.  Instances only get events when interacting with other
14649      * DragDrop object in the same group.  This lets us define multiple
14650      * groups using a single DragDrop subclass if we want.
14651      * @property groups
14652      * @type {string: string}
14653      */
14654     groups: null,
14655
14656     /**
14657      * Individual drag/drop instances can be locked.  This will prevent
14658      * onmousedown start drag.
14659      * @property locked
14660      * @type boolean
14661      * @private
14662      */
14663     locked: false,
14664
14665     /**
14666      * Lock this instance
14667      * @method lock
14668      */
14669     lock: function() { this.locked = true; },
14670
14671     /**
14672      * Unlock this instace
14673      * @method unlock
14674      */
14675     unlock: function() { this.locked = false; },
14676
14677     /**
14678      * By default, all insances can be a drop target.  This can be disabled by
14679      * setting isTarget to false.
14680      * @method isTarget
14681      * @type boolean
14682      */
14683     isTarget: true,
14684
14685     /**
14686      * The padding configured for this drag and drop object for calculating
14687      * the drop zone intersection with this object.
14688      * @method padding
14689      * @type int[]
14690      */
14691     padding: null,
14692
14693     /**
14694      * Cached reference to the linked element
14695      * @property _domRef
14696      * @private
14697      */
14698     _domRef: null,
14699
14700     /**
14701      * Internal typeof flag
14702      * @property __ygDragDrop
14703      * @private
14704      */
14705     __ygDragDrop: true,
14706
14707     /**
14708      * Set to true when horizontal contraints are applied
14709      * @property constrainX
14710      * @type boolean
14711      * @private
14712      */
14713     constrainX: false,
14714
14715     /**
14716      * Set to true when vertical contraints are applied
14717      * @property constrainY
14718      * @type boolean
14719      * @private
14720      */
14721     constrainY: false,
14722
14723     /**
14724      * The left constraint
14725      * @property minX
14726      * @type int
14727      * @private
14728      */
14729     minX: 0,
14730
14731     /**
14732      * The right constraint
14733      * @property maxX
14734      * @type int
14735      * @private
14736      */
14737     maxX: 0,
14738
14739     /**
14740      * The up constraint
14741      * @property minY
14742      * @type int
14743      * @type int
14744      * @private
14745      */
14746     minY: 0,
14747
14748     /**
14749      * The down constraint
14750      * @property maxY
14751      * @type int
14752      * @private
14753      */
14754     maxY: 0,
14755
14756     /**
14757      * Maintain offsets when we resetconstraints.  Set to true when you want
14758      * the position of the element relative to its parent to stay the same
14759      * when the page changes
14760      *
14761      * @property maintainOffset
14762      * @type boolean
14763      */
14764     maintainOffset: false,
14765
14766     /**
14767      * Array of pixel locations the element will snap to if we specified a
14768      * horizontal graduation/interval.  This array is generated automatically
14769      * when you define a tick interval.
14770      * @property xTicks
14771      * @type int[]
14772      */
14773     xTicks: null,
14774
14775     /**
14776      * Array of pixel locations the element will snap to if we specified a
14777      * vertical graduation/interval.  This array is generated automatically
14778      * when you define a tick interval.
14779      * @property yTicks
14780      * @type int[]
14781      */
14782     yTicks: null,
14783
14784     /**
14785      * By default the drag and drop instance will only respond to the primary
14786      * button click (left button for a right-handed mouse).  Set to true to
14787      * allow drag and drop to start with any mouse click that is propogated
14788      * by the browser
14789      * @property primaryButtonOnly
14790      * @type boolean
14791      */
14792     primaryButtonOnly: true,
14793
14794     /**
14795      * The availabe property is false until the linked dom element is accessible.
14796      * @property available
14797      * @type boolean
14798      */
14799     available: false,
14800
14801     /**
14802      * By default, drags can only be initiated if the mousedown occurs in the
14803      * region the linked element is.  This is done in part to work around a
14804      * bug in some browsers that mis-report the mousedown if the previous
14805      * mouseup happened outside of the window.  This property is set to true
14806      * if outer handles are defined.
14807      *
14808      * @property hasOuterHandles
14809      * @type boolean
14810      * @default false
14811      */
14812     hasOuterHandles: false,
14813
14814     /**
14815      * Code that executes immediately before the startDrag event
14816      * @method b4StartDrag
14817      * @private
14818      */
14819     b4StartDrag: function(x, y) { },
14820
14821     /**
14822      * Abstract method called after a drag/drop object is clicked
14823      * and the drag or mousedown time thresholds have beeen met.
14824      * @method startDrag
14825      * @param {int} X click location
14826      * @param {int} Y click location
14827      */
14828     startDrag: function(x, y) { /* override this */ },
14829
14830     /**
14831      * Code that executes immediately before the onDrag event
14832      * @method b4Drag
14833      * @private
14834      */
14835     b4Drag: function(e) { },
14836
14837     /**
14838      * Abstract method called during the onMouseMove event while dragging an
14839      * object.
14840      * @method onDrag
14841      * @param {Event} e the mousemove event
14842      */
14843     onDrag: function(e) { /* override this */ },
14844
14845     /**
14846      * Abstract method called when this element fist begins hovering over
14847      * another DragDrop obj
14848      * @method onDragEnter
14849      * @param {Event} e the mousemove event
14850      * @param {String|DragDrop[]} id In POINT mode, the element
14851      * id this is hovering over.  In INTERSECT mode, an array of one or more
14852      * dragdrop items being hovered over.
14853      */
14854     onDragEnter: function(e, id) { /* override this */ },
14855
14856     /**
14857      * Code that executes immediately before the onDragOver event
14858      * @method b4DragOver
14859      * @private
14860      */
14861     b4DragOver: function(e) { },
14862
14863     /**
14864      * Abstract method called when this element is hovering over another
14865      * DragDrop obj
14866      * @method onDragOver
14867      * @param {Event} e the mousemove event
14868      * @param {String|DragDrop[]} id In POINT mode, the element
14869      * id this is hovering over.  In INTERSECT mode, an array of dd items
14870      * being hovered over.
14871      */
14872     onDragOver: function(e, id) { /* override this */ },
14873
14874     /**
14875      * Code that executes immediately before the onDragOut event
14876      * @method b4DragOut
14877      * @private
14878      */
14879     b4DragOut: function(e) { },
14880
14881     /**
14882      * Abstract method called when we are no longer hovering over an element
14883      * @method onDragOut
14884      * @param {Event} e the mousemove event
14885      * @param {String|DragDrop[]} id In POINT mode, the element
14886      * id this was hovering over.  In INTERSECT mode, an array of dd items
14887      * that the mouse is no longer over.
14888      */
14889     onDragOut: function(e, id) { /* override this */ },
14890
14891     /**
14892      * Code that executes immediately before the onDragDrop event
14893      * @method b4DragDrop
14894      * @private
14895      */
14896     b4DragDrop: function(e) { },
14897
14898     /**
14899      * Abstract method called when this item is dropped on another DragDrop
14900      * obj
14901      * @method onDragDrop
14902      * @param {Event} e the mouseup event
14903      * @param {String|DragDrop[]} id In POINT mode, the element
14904      * id this was dropped on.  In INTERSECT mode, an array of dd items this
14905      * was dropped on.
14906      */
14907     onDragDrop: function(e, id) { /* override this */ },
14908
14909     /**
14910      * Abstract method called when this item is dropped on an area with no
14911      * drop target
14912      * @method onInvalidDrop
14913      * @param {Event} e the mouseup event
14914      */
14915     onInvalidDrop: function(e) { /* override this */ },
14916
14917     /**
14918      * Code that executes immediately before the endDrag event
14919      * @method b4EndDrag
14920      * @private
14921      */
14922     b4EndDrag: function(e) { },
14923
14924     /**
14925      * Fired when we are done dragging the object
14926      * @method endDrag
14927      * @param {Event} e the mouseup event
14928      */
14929     endDrag: function(e) { /* override this */ },
14930
14931     /**
14932      * Code executed immediately before the onMouseDown event
14933      * @method b4MouseDown
14934      * @param {Event} e the mousedown event
14935      * @private
14936      */
14937     b4MouseDown: function(e) {  },
14938
14939     /**
14940      * Event handler that fires when a drag/drop obj gets a mousedown
14941      * @method onMouseDown
14942      * @param {Event} e the mousedown event
14943      */
14944     onMouseDown: function(e) { /* override this */ },
14945
14946     /**
14947      * Event handler that fires when a drag/drop obj gets a mouseup
14948      * @method onMouseUp
14949      * @param {Event} e the mouseup event
14950      */
14951     onMouseUp: function(e) { /* override this */ },
14952
14953     /**
14954      * Override the onAvailable method to do what is needed after the initial
14955      * position was determined.
14956      * @method onAvailable
14957      */
14958     onAvailable: function () {
14959     },
14960
14961     /*
14962      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
14963      * @type Object
14964      */
14965     defaultPadding : {left:0, right:0, top:0, bottom:0},
14966
14967     /*
14968      * Initializes the drag drop object's constraints to restrict movement to a certain element.
14969  *
14970  * Usage:
14971  <pre><code>
14972  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
14973                 { dragElId: "existingProxyDiv" });
14974  dd.startDrag = function(){
14975      this.constrainTo("parent-id");
14976  };
14977  </code></pre>
14978  * Or you can initalize it using the {@link Roo.Element} object:
14979  <pre><code>
14980  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
14981      startDrag : function(){
14982          this.constrainTo("parent-id");
14983      }
14984  });
14985  </code></pre>
14986      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
14987      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
14988      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
14989      * an object containing the sides to pad. For example: {right:10, bottom:10}
14990      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
14991      */
14992     constrainTo : function(constrainTo, pad, inContent){
14993         if(typeof pad == "number"){
14994             pad = {left: pad, right:pad, top:pad, bottom:pad};
14995         }
14996         pad = pad || this.defaultPadding;
14997         var b = Roo.get(this.getEl()).getBox();
14998         var ce = Roo.get(constrainTo);
14999         var s = ce.getScroll();
15000         var c, cd = ce.dom;
15001         if(cd == document.body){
15002             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
15003         }else{
15004             xy = ce.getXY();
15005             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
15006         }
15007
15008
15009         var topSpace = b.y - c.y;
15010         var leftSpace = b.x - c.x;
15011
15012         this.resetConstraints();
15013         this.setXConstraint(leftSpace - (pad.left||0), // left
15014                 c.width - leftSpace - b.width - (pad.right||0) //right
15015         );
15016         this.setYConstraint(topSpace - (pad.top||0), //top
15017                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
15018         );
15019     },
15020
15021     /**
15022      * Returns a reference to the linked element
15023      * @method getEl
15024      * @return {HTMLElement} the html element
15025      */
15026     getEl: function() {
15027         if (!this._domRef) {
15028             this._domRef = Roo.getDom(this.id);
15029         }
15030
15031         return this._domRef;
15032     },
15033
15034     /**
15035      * Returns a reference to the actual element to drag.  By default this is
15036      * the same as the html element, but it can be assigned to another
15037      * element. An example of this can be found in Roo.dd.DDProxy
15038      * @method getDragEl
15039      * @return {HTMLElement} the html element
15040      */
15041     getDragEl: function() {
15042         return Roo.getDom(this.dragElId);
15043     },
15044
15045     /**
15046      * Sets up the DragDrop object.  Must be called in the constructor of any
15047      * Roo.dd.DragDrop subclass
15048      * @method init
15049      * @param id the id of the linked element
15050      * @param {String} sGroup the group of related items
15051      * @param {object} config configuration attributes
15052      */
15053     init: function(id, sGroup, config) {
15054         this.initTarget(id, sGroup, config);
15055         Event.on(this.id, "mousedown", this.handleMouseDown, this);
15056         // Event.on(this.id, "selectstart", Event.preventDefault);
15057     },
15058
15059     /**
15060      * Initializes Targeting functionality only... the object does not
15061      * get a mousedown handler.
15062      * @method initTarget
15063      * @param id the id of the linked element
15064      * @param {String} sGroup the group of related items
15065      * @param {object} config configuration attributes
15066      */
15067     initTarget: function(id, sGroup, config) {
15068
15069         // configuration attributes
15070         this.config = config || {};
15071
15072         // create a local reference to the drag and drop manager
15073         this.DDM = Roo.dd.DDM;
15074         // initialize the groups array
15075         this.groups = {};
15076
15077         // assume that we have an element reference instead of an id if the
15078         // parameter is not a string
15079         if (typeof id !== "string") {
15080             id = Roo.id(id);
15081         }
15082
15083         // set the id
15084         this.id = id;
15085
15086         // add to an interaction group
15087         this.addToGroup((sGroup) ? sGroup : "default");
15088
15089         // We don't want to register this as the handle with the manager
15090         // so we just set the id rather than calling the setter.
15091         this.handleElId = id;
15092
15093         // the linked element is the element that gets dragged by default
15094         this.setDragElId(id);
15095
15096         // by default, clicked anchors will not start drag operations.
15097         this.invalidHandleTypes = { A: "A" };
15098         this.invalidHandleIds = {};
15099         this.invalidHandleClasses = [];
15100
15101         this.applyConfig();
15102
15103         this.handleOnAvailable();
15104     },
15105
15106     /**
15107      * Applies the configuration parameters that were passed into the constructor.
15108      * This is supposed to happen at each level through the inheritance chain.  So
15109      * a DDProxy implentation will execute apply config on DDProxy, DD, and
15110      * DragDrop in order to get all of the parameters that are available in
15111      * each object.
15112      * @method applyConfig
15113      */
15114     applyConfig: function() {
15115
15116         // configurable properties:
15117         //    padding, isTarget, maintainOffset, primaryButtonOnly
15118         this.padding           = this.config.padding || [0, 0, 0, 0];
15119         this.isTarget          = (this.config.isTarget !== false);
15120         this.maintainOffset    = (this.config.maintainOffset);
15121         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
15122
15123     },
15124
15125     /**
15126      * Executed when the linked element is available
15127      * @method handleOnAvailable
15128      * @private
15129      */
15130     handleOnAvailable: function() {
15131         this.available = true;
15132         this.resetConstraints();
15133         this.onAvailable();
15134     },
15135
15136      /**
15137      * Configures the padding for the target zone in px.  Effectively expands
15138      * (or reduces) the virtual object size for targeting calculations.
15139      * Supports css-style shorthand; if only one parameter is passed, all sides
15140      * will have that padding, and if only two are passed, the top and bottom
15141      * will have the first param, the left and right the second.
15142      * @method setPadding
15143      * @param {int} iTop    Top pad
15144      * @param {int} iRight  Right pad
15145      * @param {int} iBot    Bot pad
15146      * @param {int} iLeft   Left pad
15147      */
15148     setPadding: function(iTop, iRight, iBot, iLeft) {
15149         // this.padding = [iLeft, iRight, iTop, iBot];
15150         if (!iRight && 0 !== iRight) {
15151             this.padding = [iTop, iTop, iTop, iTop];
15152         } else if (!iBot && 0 !== iBot) {
15153             this.padding = [iTop, iRight, iTop, iRight];
15154         } else {
15155             this.padding = [iTop, iRight, iBot, iLeft];
15156         }
15157     },
15158
15159     /**
15160      * Stores the initial placement of the linked element.
15161      * @method setInitialPosition
15162      * @param {int} diffX   the X offset, default 0
15163      * @param {int} diffY   the Y offset, default 0
15164      */
15165     setInitPosition: function(diffX, diffY) {
15166         var el = this.getEl();
15167
15168         if (!this.DDM.verifyEl(el)) {
15169             return;
15170         }
15171
15172         var dx = diffX || 0;
15173         var dy = diffY || 0;
15174
15175         var p = Dom.getXY( el );
15176
15177         this.initPageX = p[0] - dx;
15178         this.initPageY = p[1] - dy;
15179
15180         this.lastPageX = p[0];
15181         this.lastPageY = p[1];
15182
15183
15184         this.setStartPosition(p);
15185     },
15186
15187     /**
15188      * Sets the start position of the element.  This is set when the obj
15189      * is initialized, the reset when a drag is started.
15190      * @method setStartPosition
15191      * @param pos current position (from previous lookup)
15192      * @private
15193      */
15194     setStartPosition: function(pos) {
15195         var p = pos || Dom.getXY( this.getEl() );
15196         this.deltaSetXY = null;
15197
15198         this.startPageX = p[0];
15199         this.startPageY = p[1];
15200     },
15201
15202     /**
15203      * Add this instance to a group of related drag/drop objects.  All
15204      * instances belong to at least one group, and can belong to as many
15205      * groups as needed.
15206      * @method addToGroup
15207      * @param sGroup {string} the name of the group
15208      */
15209     addToGroup: function(sGroup) {
15210         this.groups[sGroup] = true;
15211         this.DDM.regDragDrop(this, sGroup);
15212     },
15213
15214     /**
15215      * Remove's this instance from the supplied interaction group
15216      * @method removeFromGroup
15217      * @param {string}  sGroup  The group to drop
15218      */
15219     removeFromGroup: function(sGroup) {
15220         if (this.groups[sGroup]) {
15221             delete this.groups[sGroup];
15222         }
15223
15224         this.DDM.removeDDFromGroup(this, sGroup);
15225     },
15226
15227     /**
15228      * Allows you to specify that an element other than the linked element
15229      * will be moved with the cursor during a drag
15230      * @method setDragElId
15231      * @param id {string} the id of the element that will be used to initiate the drag
15232      */
15233     setDragElId: function(id) {
15234         this.dragElId = id;
15235     },
15236
15237     /**
15238      * Allows you to specify a child of the linked element that should be
15239      * used to initiate the drag operation.  An example of this would be if
15240      * you have a content div with text and links.  Clicking anywhere in the
15241      * content area would normally start the drag operation.  Use this method
15242      * to specify that an element inside of the content div is the element
15243      * that starts the drag operation.
15244      * @method setHandleElId
15245      * @param id {string} the id of the element that will be used to
15246      * initiate the drag.
15247      */
15248     setHandleElId: function(id) {
15249         if (typeof id !== "string") {
15250             id = Roo.id(id);
15251         }
15252         this.handleElId = id;
15253         this.DDM.regHandle(this.id, id);
15254     },
15255
15256     /**
15257      * Allows you to set an element outside of the linked element as a drag
15258      * handle
15259      * @method setOuterHandleElId
15260      * @param id the id of the element that will be used to initiate the drag
15261      */
15262     setOuterHandleElId: function(id) {
15263         if (typeof id !== "string") {
15264             id = Roo.id(id);
15265         }
15266         Event.on(id, "mousedown",
15267                 this.handleMouseDown, this);
15268         this.setHandleElId(id);
15269
15270         this.hasOuterHandles = true;
15271     },
15272
15273     /**
15274      * Remove all drag and drop hooks for this element
15275      * @method unreg
15276      */
15277     unreg: function() {
15278         Event.un(this.id, "mousedown",
15279                 this.handleMouseDown);
15280         this._domRef = null;
15281         this.DDM._remove(this);
15282     },
15283
15284     destroy : function(){
15285         this.unreg();
15286     },
15287
15288     /**
15289      * Returns true if this instance is locked, or the drag drop mgr is locked
15290      * (meaning that all drag/drop is disabled on the page.)
15291      * @method isLocked
15292      * @return {boolean} true if this obj or all drag/drop is locked, else
15293      * false
15294      */
15295     isLocked: function() {
15296         return (this.DDM.isLocked() || this.locked);
15297     },
15298
15299     /**
15300      * Fired when this object is clicked
15301      * @method handleMouseDown
15302      * @param {Event} e
15303      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
15304      * @private
15305      */
15306     handleMouseDown: function(e, oDD){
15307         if (this.primaryButtonOnly && e.button != 0) {
15308             return;
15309         }
15310
15311         if (this.isLocked()) {
15312             return;
15313         }
15314
15315         this.DDM.refreshCache(this.groups);
15316
15317         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
15318         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
15319         } else {
15320             if (this.clickValidator(e)) {
15321
15322                 // set the initial element position
15323                 this.setStartPosition();
15324
15325
15326                 this.b4MouseDown(e);
15327                 this.onMouseDown(e);
15328
15329                 this.DDM.handleMouseDown(e, this);
15330
15331                 this.DDM.stopEvent(e);
15332             } else {
15333
15334
15335             }
15336         }
15337     },
15338
15339     clickValidator: function(e) {
15340         var target = e.getTarget();
15341         return ( this.isValidHandleChild(target) &&
15342                     (this.id == this.handleElId ||
15343                         this.DDM.handleWasClicked(target, this.id)) );
15344     },
15345
15346     /**
15347      * Allows you to specify a tag name that should not start a drag operation
15348      * when clicked.  This is designed to facilitate embedding links within a
15349      * drag handle that do something other than start the drag.
15350      * @method addInvalidHandleType
15351      * @param {string} tagName the type of element to exclude
15352      */
15353     addInvalidHandleType: function(tagName) {
15354         var type = tagName.toUpperCase();
15355         this.invalidHandleTypes[type] = type;
15356     },
15357
15358     /**
15359      * Lets you to specify an element id for a child of a drag handle
15360      * that should not initiate a drag
15361      * @method addInvalidHandleId
15362      * @param {string} id the element id of the element you wish to ignore
15363      */
15364     addInvalidHandleId: function(id) {
15365         if (typeof id !== "string") {
15366             id = Roo.id(id);
15367         }
15368         this.invalidHandleIds[id] = id;
15369     },
15370
15371     /**
15372      * Lets you specify a css class of elements that will not initiate a drag
15373      * @method addInvalidHandleClass
15374      * @param {string} cssClass the class of the elements you wish to ignore
15375      */
15376     addInvalidHandleClass: function(cssClass) {
15377         this.invalidHandleClasses.push(cssClass);
15378     },
15379
15380     /**
15381      * Unsets an excluded tag name set by addInvalidHandleType
15382      * @method removeInvalidHandleType
15383      * @param {string} tagName the type of element to unexclude
15384      */
15385     removeInvalidHandleType: function(tagName) {
15386         var type = tagName.toUpperCase();
15387         // this.invalidHandleTypes[type] = null;
15388         delete this.invalidHandleTypes[type];
15389     },
15390
15391     /**
15392      * Unsets an invalid handle id
15393      * @method removeInvalidHandleId
15394      * @param {string} id the id of the element to re-enable
15395      */
15396     removeInvalidHandleId: function(id) {
15397         if (typeof id !== "string") {
15398             id = Roo.id(id);
15399         }
15400         delete this.invalidHandleIds[id];
15401     },
15402
15403     /**
15404      * Unsets an invalid css class
15405      * @method removeInvalidHandleClass
15406      * @param {string} cssClass the class of the element(s) you wish to
15407      * re-enable
15408      */
15409     removeInvalidHandleClass: function(cssClass) {
15410         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
15411             if (this.invalidHandleClasses[i] == cssClass) {
15412                 delete this.invalidHandleClasses[i];
15413             }
15414         }
15415     },
15416
15417     /**
15418      * Checks the tag exclusion list to see if this click should be ignored
15419      * @method isValidHandleChild
15420      * @param {HTMLElement} node the HTMLElement to evaluate
15421      * @return {boolean} true if this is a valid tag type, false if not
15422      */
15423     isValidHandleChild: function(node) {
15424
15425         var valid = true;
15426         // var n = (node.nodeName == "#text") ? node.parentNode : node;
15427         var nodeName;
15428         try {
15429             nodeName = node.nodeName.toUpperCase();
15430         } catch(e) {
15431             nodeName = node.nodeName;
15432         }
15433         valid = valid && !this.invalidHandleTypes[nodeName];
15434         valid = valid && !this.invalidHandleIds[node.id];
15435
15436         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
15437             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
15438         }
15439
15440
15441         return valid;
15442
15443     },
15444
15445     /**
15446      * Create the array of horizontal tick marks if an interval was specified
15447      * in setXConstraint().
15448      * @method setXTicks
15449      * @private
15450      */
15451     setXTicks: function(iStartX, iTickSize) {
15452         this.xTicks = [];
15453         this.xTickSize = iTickSize;
15454
15455         var tickMap = {};
15456
15457         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
15458             if (!tickMap[i]) {
15459                 this.xTicks[this.xTicks.length] = i;
15460                 tickMap[i] = true;
15461             }
15462         }
15463
15464         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
15465             if (!tickMap[i]) {
15466                 this.xTicks[this.xTicks.length] = i;
15467                 tickMap[i] = true;
15468             }
15469         }
15470
15471         this.xTicks.sort(this.DDM.numericSort) ;
15472     },
15473
15474     /**
15475      * Create the array of vertical tick marks if an interval was specified in
15476      * setYConstraint().
15477      * @method setYTicks
15478      * @private
15479      */
15480     setYTicks: function(iStartY, iTickSize) {
15481         this.yTicks = [];
15482         this.yTickSize = iTickSize;
15483
15484         var tickMap = {};
15485
15486         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
15487             if (!tickMap[i]) {
15488                 this.yTicks[this.yTicks.length] = i;
15489                 tickMap[i] = true;
15490             }
15491         }
15492
15493         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
15494             if (!tickMap[i]) {
15495                 this.yTicks[this.yTicks.length] = i;
15496                 tickMap[i] = true;
15497             }
15498         }
15499
15500         this.yTicks.sort(this.DDM.numericSort) ;
15501     },
15502
15503     /**
15504      * By default, the element can be dragged any place on the screen.  Use
15505      * this method to limit the horizontal travel of the element.  Pass in
15506      * 0,0 for the parameters if you want to lock the drag to the y axis.
15507      * @method setXConstraint
15508      * @param {int} iLeft the number of pixels the element can move to the left
15509      * @param {int} iRight the number of pixels the element can move to the
15510      * right
15511      * @param {int} iTickSize optional parameter for specifying that the
15512      * element
15513      * should move iTickSize pixels at a time.
15514      */
15515     setXConstraint: function(iLeft, iRight, iTickSize) {
15516         this.leftConstraint = iLeft;
15517         this.rightConstraint = iRight;
15518
15519         this.minX = this.initPageX - iLeft;
15520         this.maxX = this.initPageX + iRight;
15521         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
15522
15523         this.constrainX = true;
15524     },
15525
15526     /**
15527      * Clears any constraints applied to this instance.  Also clears ticks
15528      * since they can't exist independent of a constraint at this time.
15529      * @method clearConstraints
15530      */
15531     clearConstraints: function() {
15532         this.constrainX = false;
15533         this.constrainY = false;
15534         this.clearTicks();
15535     },
15536
15537     /**
15538      * Clears any tick interval defined for this instance
15539      * @method clearTicks
15540      */
15541     clearTicks: function() {
15542         this.xTicks = null;
15543         this.yTicks = null;
15544         this.xTickSize = 0;
15545         this.yTickSize = 0;
15546     },
15547
15548     /**
15549      * By default, the element can be dragged any place on the screen.  Set
15550      * this to limit the vertical travel of the element.  Pass in 0,0 for the
15551      * parameters if you want to lock the drag to the x axis.
15552      * @method setYConstraint
15553      * @param {int} iUp the number of pixels the element can move up
15554      * @param {int} iDown the number of pixels the element can move down
15555      * @param {int} iTickSize optional parameter for specifying that the
15556      * element should move iTickSize pixels at a time.
15557      */
15558     setYConstraint: function(iUp, iDown, iTickSize) {
15559         this.topConstraint = iUp;
15560         this.bottomConstraint = iDown;
15561
15562         this.minY = this.initPageY - iUp;
15563         this.maxY = this.initPageY + iDown;
15564         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
15565
15566         this.constrainY = true;
15567
15568     },
15569
15570     /**
15571      * resetConstraints must be called if you manually reposition a dd element.
15572      * @method resetConstraints
15573      * @param {boolean} maintainOffset
15574      */
15575     resetConstraints: function() {
15576
15577
15578         // Maintain offsets if necessary
15579         if (this.initPageX || this.initPageX === 0) {
15580             // figure out how much this thing has moved
15581             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
15582             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
15583
15584             this.setInitPosition(dx, dy);
15585
15586         // This is the first time we have detected the element's position
15587         } else {
15588             this.setInitPosition();
15589         }
15590
15591         if (this.constrainX) {
15592             this.setXConstraint( this.leftConstraint,
15593                                  this.rightConstraint,
15594                                  this.xTickSize        );
15595         }
15596
15597         if (this.constrainY) {
15598             this.setYConstraint( this.topConstraint,
15599                                  this.bottomConstraint,
15600                                  this.yTickSize         );
15601         }
15602     },
15603
15604     /**
15605      * Normally the drag element is moved pixel by pixel, but we can specify
15606      * that it move a number of pixels at a time.  This method resolves the
15607      * location when we have it set up like this.
15608      * @method getTick
15609      * @param {int} val where we want to place the object
15610      * @param {int[]} tickArray sorted array of valid points
15611      * @return {int} the closest tick
15612      * @private
15613      */
15614     getTick: function(val, tickArray) {
15615
15616         if (!tickArray) {
15617             // If tick interval is not defined, it is effectively 1 pixel,
15618             // so we return the value passed to us.
15619             return val;
15620         } else if (tickArray[0] >= val) {
15621             // The value is lower than the first tick, so we return the first
15622             // tick.
15623             return tickArray[0];
15624         } else {
15625             for (var i=0, len=tickArray.length; i<len; ++i) {
15626                 var next = i + 1;
15627                 if (tickArray[next] && tickArray[next] >= val) {
15628                     var diff1 = val - tickArray[i];
15629                     var diff2 = tickArray[next] - val;
15630                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
15631                 }
15632             }
15633
15634             // The value is larger than the last tick, so we return the last
15635             // tick.
15636             return tickArray[tickArray.length - 1];
15637         }
15638     },
15639
15640     /**
15641      * toString method
15642      * @method toString
15643      * @return {string} string representation of the dd obj
15644      */
15645     toString: function() {
15646         return ("DragDrop " + this.id);
15647     }
15648
15649 });
15650
15651 })();
15652 /*
15653  * Based on:
15654  * Ext JS Library 1.1.1
15655  * Copyright(c) 2006-2007, Ext JS, LLC.
15656  *
15657  * Originally Released Under LGPL - original licence link has changed is not relivant.
15658  *
15659  * Fork - LGPL
15660  * <script type="text/javascript">
15661  */
15662
15663
15664 /**
15665  * The drag and drop utility provides a framework for building drag and drop
15666  * applications.  In addition to enabling drag and drop for specific elements,
15667  * the drag and drop elements are tracked by the manager class, and the
15668  * interactions between the various elements are tracked during the drag and
15669  * the implementing code is notified about these important moments.
15670  */
15671
15672 // Only load the library once.  Rewriting the manager class would orphan
15673 // existing drag and drop instances.
15674 if (!Roo.dd.DragDropMgr) {
15675
15676 /**
15677  * @class Roo.dd.DragDropMgr
15678  * DragDropMgr is a singleton that tracks the element interaction for
15679  * all DragDrop items in the window.  Generally, you will not call
15680  * this class directly, but it does have helper methods that could
15681  * be useful in your DragDrop implementations.
15682  * @singleton
15683  */
15684 Roo.dd.DragDropMgr = function() {
15685
15686     var Event = Roo.EventManager;
15687
15688     return {
15689
15690         /**
15691          * Two dimensional Array of registered DragDrop objects.  The first
15692          * dimension is the DragDrop item group, the second the DragDrop
15693          * object.
15694          * @property ids
15695          * @type {string: string}
15696          * @private
15697          * @static
15698          */
15699         ids: {},
15700
15701         /**
15702          * Array of element ids defined as drag handles.  Used to determine
15703          * if the element that generated the mousedown event is actually the
15704          * handle and not the html element itself.
15705          * @property handleIds
15706          * @type {string: string}
15707          * @private
15708          * @static
15709          */
15710         handleIds: {},
15711
15712         /**
15713          * the DragDrop object that is currently being dragged
15714          * @property dragCurrent
15715          * @type DragDrop
15716          * @private
15717          * @static
15718          **/
15719         dragCurrent: null,
15720
15721         /**
15722          * the DragDrop object(s) that are being hovered over
15723          * @property dragOvers
15724          * @type Array
15725          * @private
15726          * @static
15727          */
15728         dragOvers: {},
15729
15730         /**
15731          * the X distance between the cursor and the object being dragged
15732          * @property deltaX
15733          * @type int
15734          * @private
15735          * @static
15736          */
15737         deltaX: 0,
15738
15739         /**
15740          * the Y distance between the cursor and the object being dragged
15741          * @property deltaY
15742          * @type int
15743          * @private
15744          * @static
15745          */
15746         deltaY: 0,
15747
15748         /**
15749          * Flag to determine if we should prevent the default behavior of the
15750          * events we define. By default this is true, but this can be set to
15751          * false if you need the default behavior (not recommended)
15752          * @property preventDefault
15753          * @type boolean
15754          * @static
15755          */
15756         preventDefault: true,
15757
15758         /**
15759          * Flag to determine if we should stop the propagation of the events
15760          * we generate. This is true by default but you may want to set it to
15761          * false if the html element contains other features that require the
15762          * mouse click.
15763          * @property stopPropagation
15764          * @type boolean
15765          * @static
15766          */
15767         stopPropagation: true,
15768
15769         /**
15770          * Internal flag that is set to true when drag and drop has been
15771          * intialized
15772          * @property initialized
15773          * @private
15774          * @static
15775          */
15776         initalized: false,
15777
15778         /**
15779          * All drag and drop can be disabled.
15780          * @property locked
15781          * @private
15782          * @static
15783          */
15784         locked: false,
15785
15786         /**
15787          * Called the first time an element is registered.
15788          * @method init
15789          * @private
15790          * @static
15791          */
15792         init: function() {
15793             this.initialized = true;
15794         },
15795
15796         /**
15797          * In point mode, drag and drop interaction is defined by the
15798          * location of the cursor during the drag/drop
15799          * @property POINT
15800          * @type int
15801          * @static
15802          */
15803         POINT: 0,
15804
15805         /**
15806          * In intersect mode, drag and drop interactio nis defined by the
15807          * overlap of two or more drag and drop objects.
15808          * @property INTERSECT
15809          * @type int
15810          * @static
15811          */
15812         INTERSECT: 1,
15813
15814         /**
15815          * The current drag and drop mode.  Default: POINT
15816          * @property mode
15817          * @type int
15818          * @static
15819          */
15820         mode: 0,
15821
15822         /**
15823          * Runs method on all drag and drop objects
15824          * @method _execOnAll
15825          * @private
15826          * @static
15827          */
15828         _execOnAll: function(sMethod, args) {
15829             for (var i in this.ids) {
15830                 for (var j in this.ids[i]) {
15831                     var oDD = this.ids[i][j];
15832                     if (! this.isTypeOfDD(oDD)) {
15833                         continue;
15834                     }
15835                     oDD[sMethod].apply(oDD, args);
15836                 }
15837             }
15838         },
15839
15840         /**
15841          * Drag and drop initialization.  Sets up the global event handlers
15842          * @method _onLoad
15843          * @private
15844          * @static
15845          */
15846         _onLoad: function() {
15847
15848             this.init();
15849
15850
15851             Event.on(document, "mouseup",   this.handleMouseUp, this, true);
15852             Event.on(document, "mousemove", this.handleMouseMove, this, true);
15853             Event.on(window,   "unload",    this._onUnload, this, true);
15854             Event.on(window,   "resize",    this._onResize, this, true);
15855             // Event.on(window,   "mouseout",    this._test);
15856
15857         },
15858
15859         /**
15860          * Reset constraints on all drag and drop objs
15861          * @method _onResize
15862          * @private
15863          * @static
15864          */
15865         _onResize: function(e) {
15866             this._execOnAll("resetConstraints", []);
15867         },
15868
15869         /**
15870          * Lock all drag and drop functionality
15871          * @method lock
15872          * @static
15873          */
15874         lock: function() { this.locked = true; },
15875
15876         /**
15877          * Unlock all drag and drop functionality
15878          * @method unlock
15879          * @static
15880          */
15881         unlock: function() { this.locked = false; },
15882
15883         /**
15884          * Is drag and drop locked?
15885          * @method isLocked
15886          * @return {boolean} True if drag and drop is locked, false otherwise.
15887          * @static
15888          */
15889         isLocked: function() { return this.locked; },
15890
15891         /**
15892          * Location cache that is set for all drag drop objects when a drag is
15893          * initiated, cleared when the drag is finished.
15894          * @property locationCache
15895          * @private
15896          * @static
15897          */
15898         locationCache: {},
15899
15900         /**
15901          * Set useCache to false if you want to force object the lookup of each
15902          * drag and drop linked element constantly during a drag.
15903          * @property useCache
15904          * @type boolean
15905          * @static
15906          */
15907         useCache: true,
15908
15909         /**
15910          * The number of pixels that the mouse needs to move after the
15911          * mousedown before the drag is initiated.  Default=3;
15912          * @property clickPixelThresh
15913          * @type int
15914          * @static
15915          */
15916         clickPixelThresh: 3,
15917
15918         /**
15919          * The number of milliseconds after the mousedown event to initiate the
15920          * drag if we don't get a mouseup event. Default=1000
15921          * @property clickTimeThresh
15922          * @type int
15923          * @static
15924          */
15925         clickTimeThresh: 350,
15926
15927         /**
15928          * Flag that indicates that either the drag pixel threshold or the
15929          * mousdown time threshold has been met
15930          * @property dragThreshMet
15931          * @type boolean
15932          * @private
15933          * @static
15934          */
15935         dragThreshMet: false,
15936
15937         /**
15938          * Timeout used for the click time threshold
15939          * @property clickTimeout
15940          * @type Object
15941          * @private
15942          * @static
15943          */
15944         clickTimeout: null,
15945
15946         /**
15947          * The X position of the mousedown event stored for later use when a
15948          * drag threshold is met.
15949          * @property startX
15950          * @type int
15951          * @private
15952          * @static
15953          */
15954         startX: 0,
15955
15956         /**
15957          * The Y position of the mousedown event stored for later use when a
15958          * drag threshold is met.
15959          * @property startY
15960          * @type int
15961          * @private
15962          * @static
15963          */
15964         startY: 0,
15965
15966         /**
15967          * Each DragDrop instance must be registered with the DragDropMgr.
15968          * This is executed in DragDrop.init()
15969          * @method regDragDrop
15970          * @param {DragDrop} oDD the DragDrop object to register
15971          * @param {String} sGroup the name of the group this element belongs to
15972          * @static
15973          */
15974         regDragDrop: function(oDD, sGroup) {
15975             if (!this.initialized) { this.init(); }
15976
15977             if (!this.ids[sGroup]) {
15978                 this.ids[sGroup] = {};
15979             }
15980             this.ids[sGroup][oDD.id] = oDD;
15981         },
15982
15983         /**
15984          * Removes the supplied dd instance from the supplied group. Executed
15985          * by DragDrop.removeFromGroup, so don't call this function directly.
15986          * @method removeDDFromGroup
15987          * @private
15988          * @static
15989          */
15990         removeDDFromGroup: function(oDD, sGroup) {
15991             if (!this.ids[sGroup]) {
15992                 this.ids[sGroup] = {};
15993             }
15994
15995             var obj = this.ids[sGroup];
15996             if (obj && obj[oDD.id]) {
15997                 delete obj[oDD.id];
15998             }
15999         },
16000
16001         /**
16002          * Unregisters a drag and drop item.  This is executed in
16003          * DragDrop.unreg, use that method instead of calling this directly.
16004          * @method _remove
16005          * @private
16006          * @static
16007          */
16008         _remove: function(oDD) {
16009             for (var g in oDD.groups) {
16010                 if (g && this.ids[g][oDD.id]) {
16011                     delete this.ids[g][oDD.id];
16012                 }
16013             }
16014             delete this.handleIds[oDD.id];
16015         },
16016
16017         /**
16018          * Each DragDrop handle element must be registered.  This is done
16019          * automatically when executing DragDrop.setHandleElId()
16020          * @method regHandle
16021          * @param {String} sDDId the DragDrop id this element is a handle for
16022          * @param {String} sHandleId the id of the element that is the drag
16023          * handle
16024          * @static
16025          */
16026         regHandle: function(sDDId, sHandleId) {
16027             if (!this.handleIds[sDDId]) {
16028                 this.handleIds[sDDId] = {};
16029             }
16030             this.handleIds[sDDId][sHandleId] = sHandleId;
16031         },
16032
16033         /**
16034          * Utility function to determine if a given element has been
16035          * registered as a drag drop item.
16036          * @method isDragDrop
16037          * @param {String} id the element id to check
16038          * @return {boolean} true if this element is a DragDrop item,
16039          * false otherwise
16040          * @static
16041          */
16042         isDragDrop: function(id) {
16043             return ( this.getDDById(id) ) ? true : false;
16044         },
16045
16046         /**
16047          * Returns the drag and drop instances that are in all groups the
16048          * passed in instance belongs to.
16049          * @method getRelated
16050          * @param {DragDrop} p_oDD the obj to get related data for
16051          * @param {boolean} bTargetsOnly if true, only return targetable objs
16052          * @return {DragDrop[]} the related instances
16053          * @static
16054          */
16055         getRelated: function(p_oDD, bTargetsOnly) {
16056             var oDDs = [];
16057             for (var i in p_oDD.groups) {
16058                 for (j in this.ids[i]) {
16059                     var dd = this.ids[i][j];
16060                     if (! this.isTypeOfDD(dd)) {
16061                         continue;
16062                     }
16063                     if (!bTargetsOnly || dd.isTarget) {
16064                         oDDs[oDDs.length] = dd;
16065                     }
16066                 }
16067             }
16068
16069             return oDDs;
16070         },
16071
16072         /**
16073          * Returns true if the specified dd target is a legal target for
16074          * the specifice drag obj
16075          * @method isLegalTarget
16076          * @param {DragDrop} the drag obj
16077          * @param {DragDrop} the target
16078          * @return {boolean} true if the target is a legal target for the
16079          * dd obj
16080          * @static
16081          */
16082         isLegalTarget: function (oDD, oTargetDD) {
16083             var targets = this.getRelated(oDD, true);
16084             for (var i=0, len=targets.length;i<len;++i) {
16085                 if (targets[i].id == oTargetDD.id) {
16086                     return true;
16087                 }
16088             }
16089
16090             return false;
16091         },
16092
16093         /**
16094          * My goal is to be able to transparently determine if an object is
16095          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
16096          * returns "object", oDD.constructor.toString() always returns
16097          * "DragDrop" and not the name of the subclass.  So for now it just
16098          * evaluates a well-known variable in DragDrop.
16099          * @method isTypeOfDD
16100          * @param {Object} the object to evaluate
16101          * @return {boolean} true if typeof oDD = DragDrop
16102          * @static
16103          */
16104         isTypeOfDD: function (oDD) {
16105             return (oDD && oDD.__ygDragDrop);
16106         },
16107
16108         /**
16109          * Utility function to determine if a given element has been
16110          * registered as a drag drop handle for the given Drag Drop object.
16111          * @method isHandle
16112          * @param {String} id the element id to check
16113          * @return {boolean} true if this element is a DragDrop handle, false
16114          * otherwise
16115          * @static
16116          */
16117         isHandle: function(sDDId, sHandleId) {
16118             return ( this.handleIds[sDDId] &&
16119                             this.handleIds[sDDId][sHandleId] );
16120         },
16121
16122         /**
16123          * Returns the DragDrop instance for a given id
16124          * @method getDDById
16125          * @param {String} id the id of the DragDrop object
16126          * @return {DragDrop} the drag drop object, null if it is not found
16127          * @static
16128          */
16129         getDDById: function(id) {
16130             for (var i in this.ids) {
16131                 if (this.ids[i][id]) {
16132                     return this.ids[i][id];
16133                 }
16134             }
16135             return null;
16136         },
16137
16138         /**
16139          * Fired after a registered DragDrop object gets the mousedown event.
16140          * Sets up the events required to track the object being dragged
16141          * @method handleMouseDown
16142          * @param {Event} e the event
16143          * @param oDD the DragDrop object being dragged
16144          * @private
16145          * @static
16146          */
16147         handleMouseDown: function(e, oDD) {
16148             if(Roo.QuickTips){
16149                 Roo.QuickTips.disable();
16150             }
16151             this.currentTarget = e.getTarget();
16152
16153             this.dragCurrent = oDD;
16154
16155             var el = oDD.getEl();
16156
16157             // track start position
16158             this.startX = e.getPageX();
16159             this.startY = e.getPageY();
16160
16161             this.deltaX = this.startX - el.offsetLeft;
16162             this.deltaY = this.startY - el.offsetTop;
16163
16164             this.dragThreshMet = false;
16165
16166             this.clickTimeout = setTimeout(
16167                     function() {
16168                         var DDM = Roo.dd.DDM;
16169                         DDM.startDrag(DDM.startX, DDM.startY);
16170                     },
16171                     this.clickTimeThresh );
16172         },
16173
16174         /**
16175          * Fired when either the drag pixel threshol or the mousedown hold
16176          * time threshold has been met.
16177          * @method startDrag
16178          * @param x {int} the X position of the original mousedown
16179          * @param y {int} the Y position of the original mousedown
16180          * @static
16181          */
16182         startDrag: function(x, y) {
16183             clearTimeout(this.clickTimeout);
16184             if (this.dragCurrent) {
16185                 this.dragCurrent.b4StartDrag(x, y);
16186                 this.dragCurrent.startDrag(x, y);
16187             }
16188             this.dragThreshMet = true;
16189         },
16190
16191         /**
16192          * Internal function to handle the mouseup event.  Will be invoked
16193          * from the context of the document.
16194          * @method handleMouseUp
16195          * @param {Event} e the event
16196          * @private
16197          * @static
16198          */
16199         handleMouseUp: function(e) {
16200
16201             if(Roo.QuickTips){
16202                 Roo.QuickTips.enable();
16203             }
16204             if (! this.dragCurrent) {
16205                 return;
16206             }
16207
16208             clearTimeout(this.clickTimeout);
16209
16210             if (this.dragThreshMet) {
16211                 this.fireEvents(e, true);
16212             } else {
16213             }
16214
16215             this.stopDrag(e);
16216
16217             this.stopEvent(e);
16218         },
16219
16220         /**
16221          * Utility to stop event propagation and event default, if these
16222          * features are turned on.
16223          * @method stopEvent
16224          * @param {Event} e the event as returned by this.getEvent()
16225          * @static
16226          */
16227         stopEvent: function(e){
16228             if(this.stopPropagation) {
16229                 e.stopPropagation();
16230             }
16231
16232             if (this.preventDefault) {
16233                 e.preventDefault();
16234             }
16235         },
16236
16237         /**
16238          * Internal function to clean up event handlers after the drag
16239          * operation is complete
16240          * @method stopDrag
16241          * @param {Event} e the event
16242          * @private
16243          * @static
16244          */
16245         stopDrag: function(e) {
16246             // Fire the drag end event for the item that was dragged
16247             if (this.dragCurrent) {
16248                 if (this.dragThreshMet) {
16249                     this.dragCurrent.b4EndDrag(e);
16250                     this.dragCurrent.endDrag(e);
16251                 }
16252
16253                 this.dragCurrent.onMouseUp(e);
16254             }
16255
16256             this.dragCurrent = null;
16257             this.dragOvers = {};
16258         },
16259
16260         /**
16261          * Internal function to handle the mousemove event.  Will be invoked
16262          * from the context of the html element.
16263          *
16264          * @TODO figure out what we can do about mouse events lost when the
16265          * user drags objects beyond the window boundary.  Currently we can
16266          * detect this in internet explorer by verifying that the mouse is
16267          * down during the mousemove event.  Firefox doesn't give us the
16268          * button state on the mousemove event.
16269          * @method handleMouseMove
16270          * @param {Event} e the event
16271          * @private
16272          * @static
16273          */
16274         handleMouseMove: function(e) {
16275             if (! this.dragCurrent) {
16276                 return true;
16277             }
16278
16279             // var button = e.which || e.button;
16280
16281             // check for IE mouseup outside of page boundary
16282             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
16283                 this.stopEvent(e);
16284                 return this.handleMouseUp(e);
16285             }
16286
16287             if (!this.dragThreshMet) {
16288                 var diffX = Math.abs(this.startX - e.getPageX());
16289                 var diffY = Math.abs(this.startY - e.getPageY());
16290                 if (diffX > this.clickPixelThresh ||
16291                             diffY > this.clickPixelThresh) {
16292                     this.startDrag(this.startX, this.startY);
16293                 }
16294             }
16295
16296             if (this.dragThreshMet) {
16297                 this.dragCurrent.b4Drag(e);
16298                 this.dragCurrent.onDrag(e);
16299                 if(!this.dragCurrent.moveOnly){
16300                     this.fireEvents(e, false);
16301                 }
16302             }
16303
16304             this.stopEvent(e);
16305
16306             return true;
16307         },
16308
16309         /**
16310          * Iterates over all of the DragDrop elements to find ones we are
16311          * hovering over or dropping on
16312          * @method fireEvents
16313          * @param {Event} e the event
16314          * @param {boolean} isDrop is this a drop op or a mouseover op?
16315          * @private
16316          * @static
16317          */
16318         fireEvents: function(e, isDrop) {
16319             var dc = this.dragCurrent;
16320
16321             // If the user did the mouse up outside of the window, we could
16322             // get here even though we have ended the drag.
16323             if (!dc || dc.isLocked()) {
16324                 return;
16325             }
16326
16327             var pt = e.getPoint();
16328
16329             // cache the previous dragOver array
16330             var oldOvers = [];
16331
16332             var outEvts   = [];
16333             var overEvts  = [];
16334             var dropEvts  = [];
16335             var enterEvts = [];
16336
16337             // Check to see if the object(s) we were hovering over is no longer
16338             // being hovered over so we can fire the onDragOut event
16339             for (var i in this.dragOvers) {
16340
16341                 var ddo = this.dragOvers[i];
16342
16343                 if (! this.isTypeOfDD(ddo)) {
16344                     continue;
16345                 }
16346
16347                 if (! this.isOverTarget(pt, ddo, this.mode)) {
16348                     outEvts.push( ddo );
16349                 }
16350
16351                 oldOvers[i] = true;
16352                 delete this.dragOvers[i];
16353             }
16354
16355             for (var sGroup in dc.groups) {
16356
16357                 if ("string" != typeof sGroup) {
16358                     continue;
16359                 }
16360
16361                 for (i in this.ids[sGroup]) {
16362                     var oDD = this.ids[sGroup][i];
16363                     if (! this.isTypeOfDD(oDD)) {
16364                         continue;
16365                     }
16366
16367                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
16368                         if (this.isOverTarget(pt, oDD, this.mode)) {
16369                             // look for drop interactions
16370                             if (isDrop) {
16371                                 dropEvts.push( oDD );
16372                             // look for drag enter and drag over interactions
16373                             } else {
16374
16375                                 // initial drag over: dragEnter fires
16376                                 if (!oldOvers[oDD.id]) {
16377                                     enterEvts.push( oDD );
16378                                 // subsequent drag overs: dragOver fires
16379                                 } else {
16380                                     overEvts.push( oDD );
16381                                 }
16382
16383                                 this.dragOvers[oDD.id] = oDD;
16384                             }
16385                         }
16386                     }
16387                 }
16388             }
16389
16390             if (this.mode) {
16391                 if (outEvts.length) {
16392                     dc.b4DragOut(e, outEvts);
16393                     dc.onDragOut(e, outEvts);
16394                 }
16395
16396                 if (enterEvts.length) {
16397                     dc.onDragEnter(e, enterEvts);
16398                 }
16399
16400                 if (overEvts.length) {
16401                     dc.b4DragOver(e, overEvts);
16402                     dc.onDragOver(e, overEvts);
16403                 }
16404
16405                 if (dropEvts.length) {
16406                     dc.b4DragDrop(e, dropEvts);
16407                     dc.onDragDrop(e, dropEvts);
16408                 }
16409
16410             } else {
16411                 // fire dragout events
16412                 var len = 0;
16413                 for (i=0, len=outEvts.length; i<len; ++i) {
16414                     dc.b4DragOut(e, outEvts[i].id);
16415                     dc.onDragOut(e, outEvts[i].id);
16416                 }
16417
16418                 // fire enter events
16419                 for (i=0,len=enterEvts.length; i<len; ++i) {
16420                     // dc.b4DragEnter(e, oDD.id);
16421                     dc.onDragEnter(e, enterEvts[i].id);
16422                 }
16423
16424                 // fire over events
16425                 for (i=0,len=overEvts.length; i<len; ++i) {
16426                     dc.b4DragOver(e, overEvts[i].id);
16427                     dc.onDragOver(e, overEvts[i].id);
16428                 }
16429
16430                 // fire drop events
16431                 for (i=0, len=dropEvts.length; i<len; ++i) {
16432                     dc.b4DragDrop(e, dropEvts[i].id);
16433                     dc.onDragDrop(e, dropEvts[i].id);
16434                 }
16435
16436             }
16437
16438             // notify about a drop that did not find a target
16439             if (isDrop && !dropEvts.length) {
16440                 dc.onInvalidDrop(e);
16441             }
16442
16443         },
16444
16445         /**
16446          * Helper function for getting the best match from the list of drag
16447          * and drop objects returned by the drag and drop events when we are
16448          * in INTERSECT mode.  It returns either the first object that the
16449          * cursor is over, or the object that has the greatest overlap with
16450          * the dragged element.
16451          * @method getBestMatch
16452          * @param  {DragDrop[]} dds The array of drag and drop objects
16453          * targeted
16454          * @return {DragDrop}       The best single match
16455          * @static
16456          */
16457         getBestMatch: function(dds) {
16458             var winner = null;
16459             // Return null if the input is not what we expect
16460             //if (!dds || !dds.length || dds.length == 0) {
16461                // winner = null;
16462             // If there is only one item, it wins
16463             //} else if (dds.length == 1) {
16464
16465             var len = dds.length;
16466
16467             if (len == 1) {
16468                 winner = dds[0];
16469             } else {
16470                 // Loop through the targeted items
16471                 for (var i=0; i<len; ++i) {
16472                     var dd = dds[i];
16473                     // If the cursor is over the object, it wins.  If the
16474                     // cursor is over multiple matches, the first one we come
16475                     // to wins.
16476                     if (dd.cursorIsOver) {
16477                         winner = dd;
16478                         break;
16479                     // Otherwise the object with the most overlap wins
16480                     } else {
16481                         if (!winner ||
16482                             winner.overlap.getArea() < dd.overlap.getArea()) {
16483                             winner = dd;
16484                         }
16485                     }
16486                 }
16487             }
16488
16489             return winner;
16490         },
16491
16492         /**
16493          * Refreshes the cache of the top-left and bottom-right points of the
16494          * drag and drop objects in the specified group(s).  This is in the
16495          * format that is stored in the drag and drop instance, so typical
16496          * usage is:
16497          * <code>
16498          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
16499          * </code>
16500          * Alternatively:
16501          * <code>
16502          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
16503          * </code>
16504          * @TODO this really should be an indexed array.  Alternatively this
16505          * method could accept both.
16506          * @method refreshCache
16507          * @param {Object} groups an associative array of groups to refresh
16508          * @static
16509          */
16510         refreshCache: function(groups) {
16511             for (var sGroup in groups) {
16512                 if ("string" != typeof sGroup) {
16513                     continue;
16514                 }
16515                 for (var i in this.ids[sGroup]) {
16516                     var oDD = this.ids[sGroup][i];
16517
16518                     if (this.isTypeOfDD(oDD)) {
16519                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
16520                         var loc = this.getLocation(oDD);
16521                         if (loc) {
16522                             this.locationCache[oDD.id] = loc;
16523                         } else {
16524                             delete this.locationCache[oDD.id];
16525                             // this will unregister the drag and drop object if
16526                             // the element is not in a usable state
16527                             // oDD.unreg();
16528                         }
16529                     }
16530                 }
16531             }
16532         },
16533
16534         /**
16535          * This checks to make sure an element exists and is in the DOM.  The
16536          * main purpose is to handle cases where innerHTML is used to remove
16537          * drag and drop objects from the DOM.  IE provides an 'unspecified
16538          * error' when trying to access the offsetParent of such an element
16539          * @method verifyEl
16540          * @param {HTMLElement} el the element to check
16541          * @return {boolean} true if the element looks usable
16542          * @static
16543          */
16544         verifyEl: function(el) {
16545             if (el) {
16546                 var parent;
16547                 if(Roo.isIE){
16548                     try{
16549                         parent = el.offsetParent;
16550                     }catch(e){}
16551                 }else{
16552                     parent = el.offsetParent;
16553                 }
16554                 if (parent) {
16555                     return true;
16556                 }
16557             }
16558
16559             return false;
16560         },
16561
16562         /**
16563          * Returns a Region object containing the drag and drop element's position
16564          * and size, including the padding configured for it
16565          * @method getLocation
16566          * @param {DragDrop} oDD the drag and drop object to get the
16567          *                       location for
16568          * @return {Roo.lib.Region} a Region object representing the total area
16569          *                             the element occupies, including any padding
16570          *                             the instance is configured for.
16571          * @static
16572          */
16573         getLocation: function(oDD) {
16574             if (! this.isTypeOfDD(oDD)) {
16575                 return null;
16576             }
16577
16578             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
16579
16580             try {
16581                 pos= Roo.lib.Dom.getXY(el);
16582             } catch (e) { }
16583
16584             if (!pos) {
16585                 return null;
16586             }
16587
16588             x1 = pos[0];
16589             x2 = x1 + el.offsetWidth;
16590             y1 = pos[1];
16591             y2 = y1 + el.offsetHeight;
16592
16593             t = y1 - oDD.padding[0];
16594             r = x2 + oDD.padding[1];
16595             b = y2 + oDD.padding[2];
16596             l = x1 - oDD.padding[3];
16597
16598             return new Roo.lib.Region( t, r, b, l );
16599         },
16600
16601         /**
16602          * Checks the cursor location to see if it over the target
16603          * @method isOverTarget
16604          * @param {Roo.lib.Point} pt The point to evaluate
16605          * @param {DragDrop} oTarget the DragDrop object we are inspecting
16606          * @return {boolean} true if the mouse is over the target
16607          * @private
16608          * @static
16609          */
16610         isOverTarget: function(pt, oTarget, intersect) {
16611             // use cache if available
16612             var loc = this.locationCache[oTarget.id];
16613             if (!loc || !this.useCache) {
16614                 loc = this.getLocation(oTarget);
16615                 this.locationCache[oTarget.id] = loc;
16616
16617             }
16618
16619             if (!loc) {
16620                 return false;
16621             }
16622
16623             oTarget.cursorIsOver = loc.contains( pt );
16624
16625             // DragDrop is using this as a sanity check for the initial mousedown
16626             // in this case we are done.  In POINT mode, if the drag obj has no
16627             // contraints, we are also done. Otherwise we need to evaluate the
16628             // location of the target as related to the actual location of the
16629             // dragged element.
16630             var dc = this.dragCurrent;
16631             if (!dc || !dc.getTargetCoord ||
16632                     (!intersect && !dc.constrainX && !dc.constrainY)) {
16633                 return oTarget.cursorIsOver;
16634             }
16635
16636             oTarget.overlap = null;
16637
16638             // Get the current location of the drag element, this is the
16639             // location of the mouse event less the delta that represents
16640             // where the original mousedown happened on the element.  We
16641             // need to consider constraints and ticks as well.
16642             var pos = dc.getTargetCoord(pt.x, pt.y);
16643
16644             var el = dc.getDragEl();
16645             var curRegion = new Roo.lib.Region( pos.y,
16646                                                    pos.x + el.offsetWidth,
16647                                                    pos.y + el.offsetHeight,
16648                                                    pos.x );
16649
16650             var overlap = curRegion.intersect(loc);
16651
16652             if (overlap) {
16653                 oTarget.overlap = overlap;
16654                 return (intersect) ? true : oTarget.cursorIsOver;
16655             } else {
16656                 return false;
16657             }
16658         },
16659
16660         /**
16661          * unload event handler
16662          * @method _onUnload
16663          * @private
16664          * @static
16665          */
16666         _onUnload: function(e, me) {
16667             Roo.dd.DragDropMgr.unregAll();
16668         },
16669
16670         /**
16671          * Cleans up the drag and drop events and objects.
16672          * @method unregAll
16673          * @private
16674          * @static
16675          */
16676         unregAll: function() {
16677
16678             if (this.dragCurrent) {
16679                 this.stopDrag();
16680                 this.dragCurrent = null;
16681             }
16682
16683             this._execOnAll("unreg", []);
16684
16685             for (i in this.elementCache) {
16686                 delete this.elementCache[i];
16687             }
16688
16689             this.elementCache = {};
16690             this.ids = {};
16691         },
16692
16693         /**
16694          * A cache of DOM elements
16695          * @property elementCache
16696          * @private
16697          * @static
16698          */
16699         elementCache: {},
16700
16701         /**
16702          * Get the wrapper for the DOM element specified
16703          * @method getElWrapper
16704          * @param {String} id the id of the element to get
16705          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
16706          * @private
16707          * @deprecated This wrapper isn't that useful
16708          * @static
16709          */
16710         getElWrapper: function(id) {
16711             var oWrapper = this.elementCache[id];
16712             if (!oWrapper || !oWrapper.el) {
16713                 oWrapper = this.elementCache[id] =
16714                     new this.ElementWrapper(Roo.getDom(id));
16715             }
16716             return oWrapper;
16717         },
16718
16719         /**
16720          * Returns the actual DOM element
16721          * @method getElement
16722          * @param {String} id the id of the elment to get
16723          * @return {Object} The element
16724          * @deprecated use Roo.getDom instead
16725          * @static
16726          */
16727         getElement: function(id) {
16728             return Roo.getDom(id);
16729         },
16730
16731         /**
16732          * Returns the style property for the DOM element (i.e.,
16733          * document.getElById(id).style)
16734          * @method getCss
16735          * @param {String} id the id of the elment to get
16736          * @return {Object} The style property of the element
16737          * @deprecated use Roo.getDom instead
16738          * @static
16739          */
16740         getCss: function(id) {
16741             var el = Roo.getDom(id);
16742             return (el) ? el.style : null;
16743         },
16744
16745         /**
16746          * Inner class for cached elements
16747          * @class DragDropMgr.ElementWrapper
16748          * @for DragDropMgr
16749          * @private
16750          * @deprecated
16751          */
16752         ElementWrapper: function(el) {
16753                 /**
16754                  * The element
16755                  * @property el
16756                  */
16757                 this.el = el || null;
16758                 /**
16759                  * The element id
16760                  * @property id
16761                  */
16762                 this.id = this.el && el.id;
16763                 /**
16764                  * A reference to the style property
16765                  * @property css
16766                  */
16767                 this.css = this.el && el.style;
16768             },
16769
16770         /**
16771          * Returns the X position of an html element
16772          * @method getPosX
16773          * @param el the element for which to get the position
16774          * @return {int} the X coordinate
16775          * @for DragDropMgr
16776          * @deprecated use Roo.lib.Dom.getX instead
16777          * @static
16778          */
16779         getPosX: function(el) {
16780             return Roo.lib.Dom.getX(el);
16781         },
16782
16783         /**
16784          * Returns the Y position of an html element
16785          * @method getPosY
16786          * @param el the element for which to get the position
16787          * @return {int} the Y coordinate
16788          * @deprecated use Roo.lib.Dom.getY instead
16789          * @static
16790          */
16791         getPosY: function(el) {
16792             return Roo.lib.Dom.getY(el);
16793         },
16794
16795         /**
16796          * Swap two nodes.  In IE, we use the native method, for others we
16797          * emulate the IE behavior
16798          * @method swapNode
16799          * @param n1 the first node to swap
16800          * @param n2 the other node to swap
16801          * @static
16802          */
16803         swapNode: function(n1, n2) {
16804             if (n1.swapNode) {
16805                 n1.swapNode(n2);
16806             } else {
16807                 var p = n2.parentNode;
16808                 var s = n2.nextSibling;
16809
16810                 if (s == n1) {
16811                     p.insertBefore(n1, n2);
16812                 } else if (n2 == n1.nextSibling) {
16813                     p.insertBefore(n2, n1);
16814                 } else {
16815                     n1.parentNode.replaceChild(n2, n1);
16816                     p.insertBefore(n1, s);
16817                 }
16818             }
16819         },
16820
16821         /**
16822          * Returns the current scroll position
16823          * @method getScroll
16824          * @private
16825          * @static
16826          */
16827         getScroll: function () {
16828             var t, l, dde=document.documentElement, db=document.body;
16829             if (dde && (dde.scrollTop || dde.scrollLeft)) {
16830                 t = dde.scrollTop;
16831                 l = dde.scrollLeft;
16832             } else if (db) {
16833                 t = db.scrollTop;
16834                 l = db.scrollLeft;
16835             } else {
16836
16837             }
16838             return { top: t, left: l };
16839         },
16840
16841         /**
16842          * Returns the specified element style property
16843          * @method getStyle
16844          * @param {HTMLElement} el          the element
16845          * @param {string}      styleProp   the style property
16846          * @return {string} The value of the style property
16847          * @deprecated use Roo.lib.Dom.getStyle
16848          * @static
16849          */
16850         getStyle: function(el, styleProp) {
16851             return Roo.fly(el).getStyle(styleProp);
16852         },
16853
16854         /**
16855          * Gets the scrollTop
16856          * @method getScrollTop
16857          * @return {int} the document's scrollTop
16858          * @static
16859          */
16860         getScrollTop: function () { return this.getScroll().top; },
16861
16862         /**
16863          * Gets the scrollLeft
16864          * @method getScrollLeft
16865          * @return {int} the document's scrollTop
16866          * @static
16867          */
16868         getScrollLeft: function () { return this.getScroll().left; },
16869
16870         /**
16871          * Sets the x/y position of an element to the location of the
16872          * target element.
16873          * @method moveToEl
16874          * @param {HTMLElement} moveEl      The element to move
16875          * @param {HTMLElement} targetEl    The position reference element
16876          * @static
16877          */
16878         moveToEl: function (moveEl, targetEl) {
16879             var aCoord = Roo.lib.Dom.getXY(targetEl);
16880             Roo.lib.Dom.setXY(moveEl, aCoord);
16881         },
16882
16883         /**
16884          * Numeric array sort function
16885          * @method numericSort
16886          * @static
16887          */
16888         numericSort: function(a, b) { return (a - b); },
16889
16890         /**
16891          * Internal counter
16892          * @property _timeoutCount
16893          * @private
16894          * @static
16895          */
16896         _timeoutCount: 0,
16897
16898         /**
16899          * Trying to make the load order less important.  Without this we get
16900          * an error if this file is loaded before the Event Utility.
16901          * @method _addListeners
16902          * @private
16903          * @static
16904          */
16905         _addListeners: function() {
16906             var DDM = Roo.dd.DDM;
16907             if ( Roo.lib.Event && document ) {
16908                 DDM._onLoad();
16909             } else {
16910                 if (DDM._timeoutCount > 2000) {
16911                 } else {
16912                     setTimeout(DDM._addListeners, 10);
16913                     if (document && document.body) {
16914                         DDM._timeoutCount += 1;
16915                     }
16916                 }
16917             }
16918         },
16919
16920         /**
16921          * Recursively searches the immediate parent and all child nodes for
16922          * the handle element in order to determine wheter or not it was
16923          * clicked.
16924          * @method handleWasClicked
16925          * @param node the html element to inspect
16926          * @static
16927          */
16928         handleWasClicked: function(node, id) {
16929             if (this.isHandle(id, node.id)) {
16930                 return true;
16931             } else {
16932                 // check to see if this is a text node child of the one we want
16933                 var p = node.parentNode;
16934
16935                 while (p) {
16936                     if (this.isHandle(id, p.id)) {
16937                         return true;
16938                     } else {
16939                         p = p.parentNode;
16940                     }
16941                 }
16942             }
16943
16944             return false;
16945         }
16946
16947     };
16948
16949 }();
16950
16951 // shorter alias, save a few bytes
16952 Roo.dd.DDM = Roo.dd.DragDropMgr;
16953 Roo.dd.DDM._addListeners();
16954
16955 }/*
16956  * Based on:
16957  * Ext JS Library 1.1.1
16958  * Copyright(c) 2006-2007, Ext JS, LLC.
16959  *
16960  * Originally Released Under LGPL - original licence link has changed is not relivant.
16961  *
16962  * Fork - LGPL
16963  * <script type="text/javascript">
16964  */
16965
16966 /**
16967  * @class Roo.dd.DD
16968  * A DragDrop implementation where the linked element follows the
16969  * mouse cursor during a drag.
16970  * @extends Roo.dd.DragDrop
16971  * @constructor
16972  * @param {String} id the id of the linked element
16973  * @param {String} sGroup the group of related DragDrop items
16974  * @param {object} config an object containing configurable attributes
16975  *                Valid properties for DD:
16976  *                    scroll
16977  */
16978 Roo.dd.DD = function(id, sGroup, config) {
16979     if (id) {
16980         this.init(id, sGroup, config);
16981     }
16982 };
16983
16984 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
16985
16986     /**
16987      * When set to true, the utility automatically tries to scroll the browser
16988      * window wehn a drag and drop element is dragged near the viewport boundary.
16989      * Defaults to true.
16990      * @property scroll
16991      * @type boolean
16992      */
16993     scroll: true,
16994
16995     /**
16996      * Sets the pointer offset to the distance between the linked element's top
16997      * left corner and the location the element was clicked
16998      * @method autoOffset
16999      * @param {int} iPageX the X coordinate of the click
17000      * @param {int} iPageY the Y coordinate of the click
17001      */
17002     autoOffset: function(iPageX, iPageY) {
17003         var x = iPageX - this.startPageX;
17004         var y = iPageY - this.startPageY;
17005         this.setDelta(x, y);
17006     },
17007
17008     /**
17009      * Sets the pointer offset.  You can call this directly to force the
17010      * offset to be in a particular location (e.g., pass in 0,0 to set it
17011      * to the center of the object)
17012      * @method setDelta
17013      * @param {int} iDeltaX the distance from the left
17014      * @param {int} iDeltaY the distance from the top
17015      */
17016     setDelta: function(iDeltaX, iDeltaY) {
17017         this.deltaX = iDeltaX;
17018         this.deltaY = iDeltaY;
17019     },
17020
17021     /**
17022      * Sets the drag element to the location of the mousedown or click event,
17023      * maintaining the cursor location relative to the location on the element
17024      * that was clicked.  Override this if you want to place the element in a
17025      * location other than where the cursor is.
17026      * @method setDragElPos
17027      * @param {int} iPageX the X coordinate of the mousedown or drag event
17028      * @param {int} iPageY the Y coordinate of the mousedown or drag event
17029      */
17030     setDragElPos: function(iPageX, iPageY) {
17031         // the first time we do this, we are going to check to make sure
17032         // the element has css positioning
17033
17034         var el = this.getDragEl();
17035         this.alignElWithMouse(el, iPageX, iPageY);
17036     },
17037
17038     /**
17039      * Sets the element to the location of the mousedown or click event,
17040      * maintaining the cursor location relative to the location on the element
17041      * that was clicked.  Override this if you want to place the element in a
17042      * location other than where the cursor is.
17043      * @method alignElWithMouse
17044      * @param {HTMLElement} el the element to move
17045      * @param {int} iPageX the X coordinate of the mousedown or drag event
17046      * @param {int} iPageY the Y coordinate of the mousedown or drag event
17047      */
17048     alignElWithMouse: function(el, iPageX, iPageY) {
17049         var oCoord = this.getTargetCoord(iPageX, iPageY);
17050         var fly = el.dom ? el : Roo.fly(el);
17051         if (!this.deltaSetXY) {
17052             var aCoord = [oCoord.x, oCoord.y];
17053             fly.setXY(aCoord);
17054             var newLeft = fly.getLeft(true);
17055             var newTop  = fly.getTop(true);
17056             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
17057         } else {
17058             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
17059         }
17060
17061         this.cachePosition(oCoord.x, oCoord.y);
17062         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
17063         return oCoord;
17064     },
17065
17066     /**
17067      * Saves the most recent position so that we can reset the constraints and
17068      * tick marks on-demand.  We need to know this so that we can calculate the
17069      * number of pixels the element is offset from its original position.
17070      * @method cachePosition
17071      * @param iPageX the current x position (optional, this just makes it so we
17072      * don't have to look it up again)
17073      * @param iPageY the current y position (optional, this just makes it so we
17074      * don't have to look it up again)
17075      */
17076     cachePosition: function(iPageX, iPageY) {
17077         if (iPageX) {
17078             this.lastPageX = iPageX;
17079             this.lastPageY = iPageY;
17080         } else {
17081             var aCoord = Roo.lib.Dom.getXY(this.getEl());
17082             this.lastPageX = aCoord[0];
17083             this.lastPageY = aCoord[1];
17084         }
17085     },
17086
17087     /**
17088      * Auto-scroll the window if the dragged object has been moved beyond the
17089      * visible window boundary.
17090      * @method autoScroll
17091      * @param {int} x the drag element's x position
17092      * @param {int} y the drag element's y position
17093      * @param {int} h the height of the drag element
17094      * @param {int} w the width of the drag element
17095      * @private
17096      */
17097     autoScroll: function(x, y, h, w) {
17098
17099         if (this.scroll) {
17100             // The client height
17101             var clientH = Roo.lib.Dom.getViewWidth();
17102
17103             // The client width
17104             var clientW = Roo.lib.Dom.getViewHeight();
17105
17106             // The amt scrolled down
17107             var st = this.DDM.getScrollTop();
17108
17109             // The amt scrolled right
17110             var sl = this.DDM.getScrollLeft();
17111
17112             // Location of the bottom of the element
17113             var bot = h + y;
17114
17115             // Location of the right of the element
17116             var right = w + x;
17117
17118             // The distance from the cursor to the bottom of the visible area,
17119             // adjusted so that we don't scroll if the cursor is beyond the
17120             // element drag constraints
17121             var toBot = (clientH + st - y - this.deltaY);
17122
17123             // The distance from the cursor to the right of the visible area
17124             var toRight = (clientW + sl - x - this.deltaX);
17125
17126
17127             // How close to the edge the cursor must be before we scroll
17128             // var thresh = (document.all) ? 100 : 40;
17129             var thresh = 40;
17130
17131             // How many pixels to scroll per autoscroll op.  This helps to reduce
17132             // clunky scrolling. IE is more sensitive about this ... it needs this
17133             // value to be higher.
17134             var scrAmt = (document.all) ? 80 : 30;
17135
17136             // Scroll down if we are near the bottom of the visible page and the
17137             // obj extends below the crease
17138             if ( bot > clientH && toBot < thresh ) {
17139                 window.scrollTo(sl, st + scrAmt);
17140             }
17141
17142             // Scroll up if the window is scrolled down and the top of the object
17143             // goes above the top border
17144             if ( y < st && st > 0 && y - st < thresh ) {
17145                 window.scrollTo(sl, st - scrAmt);
17146             }
17147
17148             // Scroll right if the obj is beyond the right border and the cursor is
17149             // near the border.
17150             if ( right > clientW && toRight < thresh ) {
17151                 window.scrollTo(sl + scrAmt, st);
17152             }
17153
17154             // Scroll left if the window has been scrolled to the right and the obj
17155             // extends past the left border
17156             if ( x < sl && sl > 0 && x - sl < thresh ) {
17157                 window.scrollTo(sl - scrAmt, st);
17158             }
17159         }
17160     },
17161
17162     /**
17163      * Finds the location the element should be placed if we want to move
17164      * it to where the mouse location less the click offset would place us.
17165      * @method getTargetCoord
17166      * @param {int} iPageX the X coordinate of the click
17167      * @param {int} iPageY the Y coordinate of the click
17168      * @return an object that contains the coordinates (Object.x and Object.y)
17169      * @private
17170      */
17171     getTargetCoord: function(iPageX, iPageY) {
17172
17173
17174         var x = iPageX - this.deltaX;
17175         var y = iPageY - this.deltaY;
17176
17177         if (this.constrainX) {
17178             if (x < this.minX) { x = this.minX; }
17179             if (x > this.maxX) { x = this.maxX; }
17180         }
17181
17182         if (this.constrainY) {
17183             if (y < this.minY) { y = this.minY; }
17184             if (y > this.maxY) { y = this.maxY; }
17185         }
17186
17187         x = this.getTick(x, this.xTicks);
17188         y = this.getTick(y, this.yTicks);
17189
17190
17191         return {x:x, y:y};
17192     },
17193
17194     /*
17195      * Sets up config options specific to this class. Overrides
17196      * Roo.dd.DragDrop, but all versions of this method through the
17197      * inheritance chain are called
17198      */
17199     applyConfig: function() {
17200         Roo.dd.DD.superclass.applyConfig.call(this);
17201         this.scroll = (this.config.scroll !== false);
17202     },
17203
17204     /*
17205      * Event that fires prior to the onMouseDown event.  Overrides
17206      * Roo.dd.DragDrop.
17207      */
17208     b4MouseDown: function(e) {
17209         // this.resetConstraints();
17210         this.autoOffset(e.getPageX(),
17211                             e.getPageY());
17212     },
17213
17214     /*
17215      * Event that fires prior to the onDrag event.  Overrides
17216      * Roo.dd.DragDrop.
17217      */
17218     b4Drag: function(e) {
17219         this.setDragElPos(e.getPageX(),
17220                             e.getPageY());
17221     },
17222
17223     toString: function() {
17224         return ("DD " + this.id);
17225     }
17226
17227     //////////////////////////////////////////////////////////////////////////
17228     // Debugging ygDragDrop events that can be overridden
17229     //////////////////////////////////////////////////////////////////////////
17230     /*
17231     startDrag: function(x, y) {
17232     },
17233
17234     onDrag: function(e) {
17235     },
17236
17237     onDragEnter: function(e, id) {
17238     },
17239
17240     onDragOver: function(e, id) {
17241     },
17242
17243     onDragOut: function(e, id) {
17244     },
17245
17246     onDragDrop: function(e, id) {
17247     },
17248
17249     endDrag: function(e) {
17250     }
17251
17252     */
17253
17254 });/*
17255  * Based on:
17256  * Ext JS Library 1.1.1
17257  * Copyright(c) 2006-2007, Ext JS, LLC.
17258  *
17259  * Originally Released Under LGPL - original licence link has changed is not relivant.
17260  *
17261  * Fork - LGPL
17262  * <script type="text/javascript">
17263  */
17264
17265 /**
17266  * @class Roo.dd.DDProxy
17267  * A DragDrop implementation that inserts an empty, bordered div into
17268  * the document that follows the cursor during drag operations.  At the time of
17269  * the click, the frame div is resized to the dimensions of the linked html
17270  * element, and moved to the exact location of the linked element.
17271  *
17272  * References to the "frame" element refer to the single proxy element that
17273  * was created to be dragged in place of all DDProxy elements on the
17274  * page.
17275  *
17276  * @extends Roo.dd.DD
17277  * @constructor
17278  * @param {String} id the id of the linked html element
17279  * @param {String} sGroup the group of related DragDrop objects
17280  * @param {object} config an object containing configurable attributes
17281  *                Valid properties for DDProxy in addition to those in DragDrop:
17282  *                   resizeFrame, centerFrame, dragElId
17283  */
17284 Roo.dd.DDProxy = function(id, sGroup, config) {
17285     if (id) {
17286         this.init(id, sGroup, config);
17287         this.initFrame();
17288     }
17289 };
17290
17291 /**
17292  * The default drag frame div id
17293  * @property Roo.dd.DDProxy.dragElId
17294  * @type String
17295  * @static
17296  */
17297 Roo.dd.DDProxy.dragElId = "ygddfdiv";
17298
17299 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
17300
17301     /**
17302      * By default we resize the drag frame to be the same size as the element
17303      * we want to drag (this is to get the frame effect).  We can turn it off
17304      * if we want a different behavior.
17305      * @property resizeFrame
17306      * @type boolean
17307      */
17308     resizeFrame: true,
17309
17310     /**
17311      * By default the frame is positioned exactly where the drag element is, so
17312      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
17313      * you do not have constraints on the obj is to have the drag frame centered
17314      * around the cursor.  Set centerFrame to true for this effect.
17315      * @property centerFrame
17316      * @type boolean
17317      */
17318     centerFrame: false,
17319
17320     /**
17321      * Creates the proxy element if it does not yet exist
17322      * @method createFrame
17323      */
17324     createFrame: function() {
17325         var self = this;
17326         var body = document.body;
17327
17328         if (!body || !body.firstChild) {
17329             setTimeout( function() { self.createFrame(); }, 50 );
17330             return;
17331         }
17332
17333         var div = this.getDragEl();
17334
17335         if (!div) {
17336             div    = document.createElement("div");
17337             div.id = this.dragElId;
17338             var s  = div.style;
17339
17340             s.position   = "absolute";
17341             s.visibility = "hidden";
17342             s.cursor     = "move";
17343             s.border     = "2px solid #aaa";
17344             s.zIndex     = 999;
17345
17346             // appendChild can blow up IE if invoked prior to the window load event
17347             // while rendering a table.  It is possible there are other scenarios
17348             // that would cause this to happen as well.
17349             body.insertBefore(div, body.firstChild);
17350         }
17351     },
17352
17353     /**
17354      * Initialization for the drag frame element.  Must be called in the
17355      * constructor of all subclasses
17356      * @method initFrame
17357      */
17358     initFrame: function() {
17359         this.createFrame();
17360     },
17361
17362     applyConfig: function() {
17363         Roo.dd.DDProxy.superclass.applyConfig.call(this);
17364
17365         this.resizeFrame = (this.config.resizeFrame !== false);
17366         this.centerFrame = (this.config.centerFrame);
17367         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
17368     },
17369
17370     /**
17371      * Resizes the drag frame to the dimensions of the clicked object, positions
17372      * it over the object, and finally displays it
17373      * @method showFrame
17374      * @param {int} iPageX X click position
17375      * @param {int} iPageY Y click position
17376      * @private
17377      */
17378     showFrame: function(iPageX, iPageY) {
17379         var el = this.getEl();
17380         var dragEl = this.getDragEl();
17381         var s = dragEl.style;
17382
17383         this._resizeProxy();
17384
17385         if (this.centerFrame) {
17386             this.setDelta( Math.round(parseInt(s.width,  10)/2),
17387                            Math.round(parseInt(s.height, 10)/2) );
17388         }
17389
17390         this.setDragElPos(iPageX, iPageY);
17391
17392         Roo.fly(dragEl).show();
17393     },
17394
17395     /**
17396      * The proxy is automatically resized to the dimensions of the linked
17397      * element when a drag is initiated, unless resizeFrame is set to false
17398      * @method _resizeProxy
17399      * @private
17400      */
17401     _resizeProxy: function() {
17402         if (this.resizeFrame) {
17403             var el = this.getEl();
17404             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
17405         }
17406     },
17407
17408     // overrides Roo.dd.DragDrop
17409     b4MouseDown: function(e) {
17410         var x = e.getPageX();
17411         var y = e.getPageY();
17412         this.autoOffset(x, y);
17413         this.setDragElPos(x, y);
17414     },
17415
17416     // overrides Roo.dd.DragDrop
17417     b4StartDrag: function(x, y) {
17418         // show the drag frame
17419         this.showFrame(x, y);
17420     },
17421
17422     // overrides Roo.dd.DragDrop
17423     b4EndDrag: function(e) {
17424         Roo.fly(this.getDragEl()).hide();
17425     },
17426
17427     // overrides Roo.dd.DragDrop
17428     // By default we try to move the element to the last location of the frame.
17429     // This is so that the default behavior mirrors that of Roo.dd.DD.
17430     endDrag: function(e) {
17431
17432         var lel = this.getEl();
17433         var del = this.getDragEl();
17434
17435         // Show the drag frame briefly so we can get its position
17436         del.style.visibility = "";
17437
17438         this.beforeMove();
17439         // Hide the linked element before the move to get around a Safari
17440         // rendering bug.
17441         lel.style.visibility = "hidden";
17442         Roo.dd.DDM.moveToEl(lel, del);
17443         del.style.visibility = "hidden";
17444         lel.style.visibility = "";
17445
17446         this.afterDrag();
17447     },
17448
17449     beforeMove : function(){
17450
17451     },
17452
17453     afterDrag : function(){
17454
17455     },
17456
17457     toString: function() {
17458         return ("DDProxy " + this.id);
17459     }
17460
17461 });
17462 /*
17463  * Based on:
17464  * Ext JS Library 1.1.1
17465  * Copyright(c) 2006-2007, Ext JS, LLC.
17466  *
17467  * Originally Released Under LGPL - original licence link has changed is not relivant.
17468  *
17469  * Fork - LGPL
17470  * <script type="text/javascript">
17471  */
17472
17473  /**
17474  * @class Roo.dd.DDTarget
17475  * A DragDrop implementation that does not move, but can be a drop
17476  * target.  You would get the same result by simply omitting implementation
17477  * for the event callbacks, but this way we reduce the processing cost of the
17478  * event listener and the callbacks.
17479  * @extends Roo.dd.DragDrop
17480  * @constructor
17481  * @param {String} id the id of the element that is a drop target
17482  * @param {String} sGroup the group of related DragDrop objects
17483  * @param {object} config an object containing configurable attributes
17484  *                 Valid properties for DDTarget in addition to those in
17485  *                 DragDrop:
17486  *                    none
17487  */
17488 Roo.dd.DDTarget = function(id, sGroup, config) {
17489     if (id) {
17490         this.initTarget(id, sGroup, config);
17491     }
17492     if (config.listeners || config.events) { 
17493        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
17494             listeners : config.listeners || {}, 
17495             events : config.events || {} 
17496         });    
17497     }
17498 };
17499
17500 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
17501 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
17502     toString: function() {
17503         return ("DDTarget " + this.id);
17504     }
17505 });
17506 /*
17507  * Based on:
17508  * Ext JS Library 1.1.1
17509  * Copyright(c) 2006-2007, Ext JS, LLC.
17510  *
17511  * Originally Released Under LGPL - original licence link has changed is not relivant.
17512  *
17513  * Fork - LGPL
17514  * <script type="text/javascript">
17515  */
17516  
17517
17518 /**
17519  * @class Roo.dd.ScrollManager
17520  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
17521  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
17522  * @singleton
17523  */
17524 Roo.dd.ScrollManager = function(){
17525     var ddm = Roo.dd.DragDropMgr;
17526     var els = {};
17527     var dragEl = null;
17528     var proc = {};
17529     
17530     var onStop = function(e){
17531         dragEl = null;
17532         clearProc();
17533     };
17534     
17535     var triggerRefresh = function(){
17536         if(ddm.dragCurrent){
17537              ddm.refreshCache(ddm.dragCurrent.groups);
17538         }
17539     };
17540     
17541     var doScroll = function(){
17542         if(ddm.dragCurrent){
17543             var dds = Roo.dd.ScrollManager;
17544             if(!dds.animate){
17545                 if(proc.el.scroll(proc.dir, dds.increment)){
17546                     triggerRefresh();
17547                 }
17548             }else{
17549                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
17550             }
17551         }
17552     };
17553     
17554     var clearProc = function(){
17555         if(proc.id){
17556             clearInterval(proc.id);
17557         }
17558         proc.id = 0;
17559         proc.el = null;
17560         proc.dir = "";
17561     };
17562     
17563     var startProc = function(el, dir){
17564         clearProc();
17565         proc.el = el;
17566         proc.dir = dir;
17567         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
17568     };
17569     
17570     var onFire = function(e, isDrop){
17571         if(isDrop || !ddm.dragCurrent){ return; }
17572         var dds = Roo.dd.ScrollManager;
17573         if(!dragEl || dragEl != ddm.dragCurrent){
17574             dragEl = ddm.dragCurrent;
17575             // refresh regions on drag start
17576             dds.refreshCache();
17577         }
17578         
17579         var xy = Roo.lib.Event.getXY(e);
17580         var pt = new Roo.lib.Point(xy[0], xy[1]);
17581         for(var id in els){
17582             var el = els[id], r = el._region;
17583             if(r && r.contains(pt) && el.isScrollable()){
17584                 if(r.bottom - pt.y <= dds.thresh){
17585                     if(proc.el != el){
17586                         startProc(el, "down");
17587                     }
17588                     return;
17589                 }else if(r.right - pt.x <= dds.thresh){
17590                     if(proc.el != el){
17591                         startProc(el, "left");
17592                     }
17593                     return;
17594                 }else if(pt.y - r.top <= dds.thresh){
17595                     if(proc.el != el){
17596                         startProc(el, "up");
17597                     }
17598                     return;
17599                 }else if(pt.x - r.left <= dds.thresh){
17600                     if(proc.el != el){
17601                         startProc(el, "right");
17602                     }
17603                     return;
17604                 }
17605             }
17606         }
17607         clearProc();
17608     };
17609     
17610     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
17611     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
17612     
17613     return {
17614         /**
17615          * Registers new overflow element(s) to auto scroll
17616          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
17617          */
17618         register : function(el){
17619             if(el instanceof Array){
17620                 for(var i = 0, len = el.length; i < len; i++) {
17621                         this.register(el[i]);
17622                 }
17623             }else{
17624                 el = Roo.get(el);
17625                 els[el.id] = el;
17626             }
17627         },
17628         
17629         /**
17630          * Unregisters overflow element(s) so they are no longer scrolled
17631          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
17632          */
17633         unregister : function(el){
17634             if(el instanceof Array){
17635                 for(var i = 0, len = el.length; i < len; i++) {
17636                         this.unregister(el[i]);
17637                 }
17638             }else{
17639                 el = Roo.get(el);
17640                 delete els[el.id];
17641             }
17642         },
17643         
17644         /**
17645          * The number of pixels from the edge of a container the pointer needs to be to 
17646          * trigger scrolling (defaults to 25)
17647          * @type Number
17648          */
17649         thresh : 25,
17650         
17651         /**
17652          * The number of pixels to scroll in each scroll increment (defaults to 50)
17653          * @type Number
17654          */
17655         increment : 100,
17656         
17657         /**
17658          * The frequency of scrolls in milliseconds (defaults to 500)
17659          * @type Number
17660          */
17661         frequency : 500,
17662         
17663         /**
17664          * True to animate the scroll (defaults to true)
17665          * @type Boolean
17666          */
17667         animate: true,
17668         
17669         /**
17670          * The animation duration in seconds - 
17671          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
17672          * @type Number
17673          */
17674         animDuration: .4,
17675         
17676         /**
17677          * Manually trigger a cache refresh.
17678          */
17679         refreshCache : function(){
17680             for(var id in els){
17681                 if(typeof els[id] == 'object'){ // for people extending the object prototype
17682                     els[id]._region = els[id].getRegion();
17683                 }
17684             }
17685         }
17686     };
17687 }();/*
17688  * Based on:
17689  * Ext JS Library 1.1.1
17690  * Copyright(c) 2006-2007, Ext JS, LLC.
17691  *
17692  * Originally Released Under LGPL - original licence link has changed is not relivant.
17693  *
17694  * Fork - LGPL
17695  * <script type="text/javascript">
17696  */
17697  
17698
17699 /**
17700  * @class Roo.dd.Registry
17701  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
17702  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
17703  * @singleton
17704  */
17705 Roo.dd.Registry = function(){
17706     var elements = {}; 
17707     var handles = {}; 
17708     var autoIdSeed = 0;
17709
17710     var getId = function(el, autogen){
17711         if(typeof el == "string"){
17712             return el;
17713         }
17714         var id = el.id;
17715         if(!id && autogen !== false){
17716             id = "roodd-" + (++autoIdSeed);
17717             el.id = id;
17718         }
17719         return id;
17720     };
17721     
17722     return {
17723     /**
17724      * Register a drag drop element
17725      * @param {String|HTMLElement} element The id or DOM node to register
17726      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
17727      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
17728      * knows how to interpret, plus there are some specific properties known to the Registry that should be
17729      * populated in the data object (if applicable):
17730      * <pre>
17731 Value      Description<br />
17732 ---------  ------------------------------------------<br />
17733 handles    Array of DOM nodes that trigger dragging<br />
17734            for the element being registered<br />
17735 isHandle   True if the element passed in triggers<br />
17736            dragging itself, else false
17737 </pre>
17738      */
17739         register : function(el, data){
17740             data = data || {};
17741             if(typeof el == "string"){
17742                 el = document.getElementById(el);
17743             }
17744             data.ddel = el;
17745             elements[getId(el)] = data;
17746             if(data.isHandle !== false){
17747                 handles[data.ddel.id] = data;
17748             }
17749             if(data.handles){
17750                 var hs = data.handles;
17751                 for(var i = 0, len = hs.length; i < len; i++){
17752                         handles[getId(hs[i])] = data;
17753                 }
17754             }
17755         },
17756
17757     /**
17758      * Unregister a drag drop element
17759      * @param {String|HTMLElement}  element The id or DOM node to unregister
17760      */
17761         unregister : function(el){
17762             var id = getId(el, false);
17763             var data = elements[id];
17764             if(data){
17765                 delete elements[id];
17766                 if(data.handles){
17767                     var hs = data.handles;
17768                     for(var i = 0, len = hs.length; i < len; i++){
17769                         delete handles[getId(hs[i], false)];
17770                     }
17771                 }
17772             }
17773         },
17774
17775     /**
17776      * Returns the handle registered for a DOM Node by id
17777      * @param {String|HTMLElement} id The DOM node or id to look up
17778      * @return {Object} handle The custom handle data
17779      */
17780         getHandle : function(id){
17781             if(typeof id != "string"){ // must be element?
17782                 id = id.id;
17783             }
17784             return handles[id];
17785         },
17786
17787     /**
17788      * Returns the handle that is registered for the DOM node that is the target of the event
17789      * @param {Event} e The event
17790      * @return {Object} handle The custom handle data
17791      */
17792         getHandleFromEvent : function(e){
17793             var t = Roo.lib.Event.getTarget(e);
17794             return t ? handles[t.id] : null;
17795         },
17796
17797     /**
17798      * Returns a custom data object that is registered for a DOM node by id
17799      * @param {String|HTMLElement} id The DOM node or id to look up
17800      * @return {Object} data The custom data
17801      */
17802         getTarget : function(id){
17803             if(typeof id != "string"){ // must be element?
17804                 id = id.id;
17805             }
17806             return elements[id];
17807         },
17808
17809     /**
17810      * Returns a custom data object that is registered for the DOM node that is the target of the event
17811      * @param {Event} e The event
17812      * @return {Object} data The custom data
17813      */
17814         getTargetFromEvent : function(e){
17815             var t = Roo.lib.Event.getTarget(e);
17816             return t ? elements[t.id] || handles[t.id] : null;
17817         }
17818     };
17819 }();/*
17820  * Based on:
17821  * Ext JS Library 1.1.1
17822  * Copyright(c) 2006-2007, Ext JS, LLC.
17823  *
17824  * Originally Released Under LGPL - original licence link has changed is not relivant.
17825  *
17826  * Fork - LGPL
17827  * <script type="text/javascript">
17828  */
17829  
17830
17831 /**
17832  * @class Roo.dd.StatusProxy
17833  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
17834  * default drag proxy used by all Roo.dd components.
17835  * @constructor
17836  * @param {Object} config
17837  */
17838 Roo.dd.StatusProxy = function(config){
17839     Roo.apply(this, config);
17840     this.id = this.id || Roo.id();
17841     this.el = new Roo.Layer({
17842         dh: {
17843             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
17844                 {tag: "div", cls: "x-dd-drop-icon"},
17845                 {tag: "div", cls: "x-dd-drag-ghost"}
17846             ]
17847         }, 
17848         shadow: !config || config.shadow !== false
17849     });
17850     this.ghost = Roo.get(this.el.dom.childNodes[1]);
17851     this.dropStatus = this.dropNotAllowed;
17852 };
17853
17854 Roo.dd.StatusProxy.prototype = {
17855     /**
17856      * @cfg {String} dropAllowed
17857      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
17858      */
17859     dropAllowed : "x-dd-drop-ok",
17860     /**
17861      * @cfg {String} dropNotAllowed
17862      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
17863      */
17864     dropNotAllowed : "x-dd-drop-nodrop",
17865
17866     /**
17867      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
17868      * over the current target element.
17869      * @param {String} cssClass The css class for the new drop status indicator image
17870      */
17871     setStatus : function(cssClass){
17872         cssClass = cssClass || this.dropNotAllowed;
17873         if(this.dropStatus != cssClass){
17874             this.el.replaceClass(this.dropStatus, cssClass);
17875             this.dropStatus = cssClass;
17876         }
17877     },
17878
17879     /**
17880      * Resets the status indicator to the default dropNotAllowed value
17881      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
17882      */
17883     reset : function(clearGhost){
17884         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
17885         this.dropStatus = this.dropNotAllowed;
17886         if(clearGhost){
17887             this.ghost.update("");
17888         }
17889     },
17890
17891     /**
17892      * Updates the contents of the ghost element
17893      * @param {String} html The html that will replace the current innerHTML of the ghost element
17894      */
17895     update : function(html){
17896         if(typeof html == "string"){
17897             this.ghost.update(html);
17898         }else{
17899             this.ghost.update("");
17900             html.style.margin = "0";
17901             this.ghost.dom.appendChild(html);
17902         }
17903         // ensure float = none set?? cant remember why though.
17904         var el = this.ghost.dom.firstChild;
17905                 if(el){
17906                         Roo.fly(el).setStyle('float', 'none');
17907                 }
17908     },
17909     
17910     /**
17911      * Returns the underlying proxy {@link Roo.Layer}
17912      * @return {Roo.Layer} el
17913     */
17914     getEl : function(){
17915         return this.el;
17916     },
17917
17918     /**
17919      * Returns the ghost element
17920      * @return {Roo.Element} el
17921      */
17922     getGhost : function(){
17923         return this.ghost;
17924     },
17925
17926     /**
17927      * Hides the proxy
17928      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
17929      */
17930     hide : function(clear){
17931         this.el.hide();
17932         if(clear){
17933             this.reset(true);
17934         }
17935     },
17936
17937     /**
17938      * Stops the repair animation if it's currently running
17939      */
17940     stop : function(){
17941         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
17942             this.anim.stop();
17943         }
17944     },
17945
17946     /**
17947      * Displays this proxy
17948      */
17949     show : function(){
17950         this.el.show();
17951     },
17952
17953     /**
17954      * Force the Layer to sync its shadow and shim positions to the element
17955      */
17956     sync : function(){
17957         this.el.sync();
17958     },
17959
17960     /**
17961      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
17962      * invalid drop operation by the item being dragged.
17963      * @param {Array} xy The XY position of the element ([x, y])
17964      * @param {Function} callback The function to call after the repair is complete
17965      * @param {Object} scope The scope in which to execute the callback
17966      */
17967     repair : function(xy, callback, scope){
17968         this.callback = callback;
17969         this.scope = scope;
17970         if(xy && this.animRepair !== false){
17971             this.el.addClass("x-dd-drag-repair");
17972             this.el.hideUnders(true);
17973             this.anim = this.el.shift({
17974                 duration: this.repairDuration || .5,
17975                 easing: 'easeOut',
17976                 xy: xy,
17977                 stopFx: true,
17978                 callback: this.afterRepair,
17979                 scope: this
17980             });
17981         }else{
17982             this.afterRepair();
17983         }
17984     },
17985
17986     // private
17987     afterRepair : function(){
17988         this.hide(true);
17989         if(typeof this.callback == "function"){
17990             this.callback.call(this.scope || this);
17991         }
17992         this.callback = null;
17993         this.scope = null;
17994     }
17995 };/*
17996  * Based on:
17997  * Ext JS Library 1.1.1
17998  * Copyright(c) 2006-2007, Ext JS, LLC.
17999  *
18000  * Originally Released Under LGPL - original licence link has changed is not relivant.
18001  *
18002  * Fork - LGPL
18003  * <script type="text/javascript">
18004  */
18005
18006 /**
18007  * @class Roo.dd.DragSource
18008  * @extends Roo.dd.DDProxy
18009  * A simple class that provides the basic implementation needed to make any element draggable.
18010  * @constructor
18011  * @param {String/HTMLElement/Element} el The container element
18012  * @param {Object} config
18013  */
18014 Roo.dd.DragSource = function(el, config){
18015     this.el = Roo.get(el);
18016     this.dragData = {};
18017     
18018     Roo.apply(this, config);
18019     
18020     if(!this.proxy){
18021         this.proxy = new Roo.dd.StatusProxy();
18022     }
18023
18024     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
18025           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
18026     
18027     this.dragging = false;
18028 };
18029
18030 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
18031     /**
18032      * @cfg {String} dropAllowed
18033      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
18034      */
18035     dropAllowed : "x-dd-drop-ok",
18036     /**
18037      * @cfg {String} dropNotAllowed
18038      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
18039      */
18040     dropNotAllowed : "x-dd-drop-nodrop",
18041
18042     /**
18043      * Returns the data object associated with this drag source
18044      * @return {Object} data An object containing arbitrary data
18045      */
18046     getDragData : function(e){
18047         return this.dragData;
18048     },
18049
18050     // private
18051     onDragEnter : function(e, id){
18052         var target = Roo.dd.DragDropMgr.getDDById(id);
18053         this.cachedTarget = target;
18054         if(this.beforeDragEnter(target, e, id) !== false){
18055             if(target.isNotifyTarget){
18056                 var status = target.notifyEnter(this, e, this.dragData);
18057                 this.proxy.setStatus(status);
18058             }else{
18059                 this.proxy.setStatus(this.dropAllowed);
18060             }
18061             
18062             if(this.afterDragEnter){
18063                 /**
18064                  * An empty function by default, but provided so that you can perform a custom action
18065                  * when the dragged item enters the drop target by providing an implementation.
18066                  * @param {Roo.dd.DragDrop} target The drop target
18067                  * @param {Event} e The event object
18068                  * @param {String} id The id of the dragged element
18069                  * @method afterDragEnter
18070                  */
18071                 this.afterDragEnter(target, e, id);
18072             }
18073         }
18074     },
18075
18076     /**
18077      * An empty function by default, but provided so that you can perform a custom action
18078      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
18079      * @param {Roo.dd.DragDrop} target The drop target
18080      * @param {Event} e The event object
18081      * @param {String} id The id of the dragged element
18082      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18083      */
18084     beforeDragEnter : function(target, e, id){
18085         return true;
18086     },
18087
18088     // private
18089     alignElWithMouse: function() {
18090         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
18091         this.proxy.sync();
18092     },
18093
18094     // private
18095     onDragOver : function(e, id){
18096         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18097         if(this.beforeDragOver(target, e, id) !== false){
18098             if(target.isNotifyTarget){
18099                 var status = target.notifyOver(this, e, this.dragData);
18100                 this.proxy.setStatus(status);
18101             }
18102
18103             if(this.afterDragOver){
18104                 /**
18105                  * An empty function by default, but provided so that you can perform a custom action
18106                  * while the dragged item is over the drop target by providing an implementation.
18107                  * @param {Roo.dd.DragDrop} target The drop target
18108                  * @param {Event} e The event object
18109                  * @param {String} id The id of the dragged element
18110                  * @method afterDragOver
18111                  */
18112                 this.afterDragOver(target, e, id);
18113             }
18114         }
18115     },
18116
18117     /**
18118      * An empty function by default, but provided so that you can perform a custom action
18119      * while the dragged item is over the drop target and optionally cancel the onDragOver.
18120      * @param {Roo.dd.DragDrop} target The drop target
18121      * @param {Event} e The event object
18122      * @param {String} id The id of the dragged element
18123      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18124      */
18125     beforeDragOver : function(target, e, id){
18126         return true;
18127     },
18128
18129     // private
18130     onDragOut : function(e, id){
18131         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18132         if(this.beforeDragOut(target, e, id) !== false){
18133             if(target.isNotifyTarget){
18134                 target.notifyOut(this, e, this.dragData);
18135             }
18136             this.proxy.reset();
18137             if(this.afterDragOut){
18138                 /**
18139                  * An empty function by default, but provided so that you can perform a custom action
18140                  * after the dragged item is dragged out of the target without dropping.
18141                  * @param {Roo.dd.DragDrop} target The drop target
18142                  * @param {Event} e The event object
18143                  * @param {String} id The id of the dragged element
18144                  * @method afterDragOut
18145                  */
18146                 this.afterDragOut(target, e, id);
18147             }
18148         }
18149         this.cachedTarget = null;
18150     },
18151
18152     /**
18153      * An empty function by default, but provided so that you can perform a custom action before the dragged
18154      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
18155      * @param {Roo.dd.DragDrop} target The drop target
18156      * @param {Event} e The event object
18157      * @param {String} id The id of the dragged element
18158      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18159      */
18160     beforeDragOut : function(target, e, id){
18161         return true;
18162     },
18163     
18164     // private
18165     onDragDrop : function(e, id){
18166         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18167         if(this.beforeDragDrop(target, e, id) !== false){
18168             if(target.isNotifyTarget){
18169                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
18170                     this.onValidDrop(target, e, id);
18171                 }else{
18172                     this.onInvalidDrop(target, e, id);
18173                 }
18174             }else{
18175                 this.onValidDrop(target, e, id);
18176             }
18177             
18178             if(this.afterDragDrop){
18179                 /**
18180                  * An empty function by default, but provided so that you can perform a custom action
18181                  * after a valid drag drop has occurred by providing an implementation.
18182                  * @param {Roo.dd.DragDrop} target The drop target
18183                  * @param {Event} e The event object
18184                  * @param {String} id The id of the dropped element
18185                  * @method afterDragDrop
18186                  */
18187                 this.afterDragDrop(target, e, id);
18188             }
18189         }
18190         delete this.cachedTarget;
18191     },
18192
18193     /**
18194      * An empty function by default, but provided so that you can perform a custom action before the dragged
18195      * item is dropped onto the target and optionally cancel the onDragDrop.
18196      * @param {Roo.dd.DragDrop} target The drop target
18197      * @param {Event} e The event object
18198      * @param {String} id The id of the dragged element
18199      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
18200      */
18201     beforeDragDrop : function(target, e, id){
18202         return true;
18203     },
18204
18205     // private
18206     onValidDrop : function(target, e, id){
18207         this.hideProxy();
18208         if(this.afterValidDrop){
18209             /**
18210              * An empty function by default, but provided so that you can perform a custom action
18211              * after a valid drop has occurred by providing an implementation.
18212              * @param {Object} target The target DD 
18213              * @param {Event} e The event object
18214              * @param {String} id The id of the dropped element
18215              * @method afterInvalidDrop
18216              */
18217             this.afterValidDrop(target, e, id);
18218         }
18219     },
18220
18221     // private
18222     getRepairXY : function(e, data){
18223         return this.el.getXY();  
18224     },
18225
18226     // private
18227     onInvalidDrop : function(target, e, id){
18228         this.beforeInvalidDrop(target, e, id);
18229         if(this.cachedTarget){
18230             if(this.cachedTarget.isNotifyTarget){
18231                 this.cachedTarget.notifyOut(this, e, this.dragData);
18232             }
18233             this.cacheTarget = null;
18234         }
18235         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
18236
18237         if(this.afterInvalidDrop){
18238             /**
18239              * An empty function by default, but provided so that you can perform a custom action
18240              * after an invalid drop has occurred by providing an implementation.
18241              * @param {Event} e The event object
18242              * @param {String} id The id of the dropped element
18243              * @method afterInvalidDrop
18244              */
18245             this.afterInvalidDrop(e, id);
18246         }
18247     },
18248
18249     // private
18250     afterRepair : function(){
18251         if(Roo.enableFx){
18252             this.el.highlight(this.hlColor || "c3daf9");
18253         }
18254         this.dragging = false;
18255     },
18256
18257     /**
18258      * An empty function by default, but provided so that you can perform a custom action after an invalid
18259      * drop has occurred.
18260      * @param {Roo.dd.DragDrop} target The drop target
18261      * @param {Event} e The event object
18262      * @param {String} id The id of the dragged element
18263      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
18264      */
18265     beforeInvalidDrop : function(target, e, id){
18266         return true;
18267     },
18268
18269     // private
18270     handleMouseDown : function(e){
18271         if(this.dragging) {
18272             return;
18273         }
18274         var data = this.getDragData(e);
18275         if(data && this.onBeforeDrag(data, e) !== false){
18276             this.dragData = data;
18277             this.proxy.stop();
18278             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
18279         } 
18280     },
18281
18282     /**
18283      * An empty function by default, but provided so that you can perform a custom action before the initial
18284      * drag event begins and optionally cancel it.
18285      * @param {Object} data An object containing arbitrary data to be shared with drop targets
18286      * @param {Event} e The event object
18287      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18288      */
18289     onBeforeDrag : function(data, e){
18290         return true;
18291     },
18292
18293     /**
18294      * An empty function by default, but provided so that you can perform a custom action once the initial
18295      * drag event has begun.  The drag cannot be canceled from this function.
18296      * @param {Number} x The x position of the click on the dragged object
18297      * @param {Number} y The y position of the click on the dragged object
18298      */
18299     onStartDrag : Roo.emptyFn,
18300
18301     // private - YUI override
18302     startDrag : function(x, y){
18303         this.proxy.reset();
18304         this.dragging = true;
18305         this.proxy.update("");
18306         this.onInitDrag(x, y);
18307         this.proxy.show();
18308     },
18309
18310     // private
18311     onInitDrag : function(x, y){
18312         var clone = this.el.dom.cloneNode(true);
18313         clone.id = Roo.id(); // prevent duplicate ids
18314         this.proxy.update(clone);
18315         this.onStartDrag(x, y);
18316         return true;
18317     },
18318
18319     /**
18320      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
18321      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
18322      */
18323     getProxy : function(){
18324         return this.proxy;  
18325     },
18326
18327     /**
18328      * Hides the drag source's {@link Roo.dd.StatusProxy}
18329      */
18330     hideProxy : function(){
18331         this.proxy.hide();  
18332         this.proxy.reset(true);
18333         this.dragging = false;
18334     },
18335
18336     // private
18337     triggerCacheRefresh : function(){
18338         Roo.dd.DDM.refreshCache(this.groups);
18339     },
18340
18341     // private - override to prevent hiding
18342     b4EndDrag: function(e) {
18343     },
18344
18345     // private - override to prevent moving
18346     endDrag : function(e){
18347         this.onEndDrag(this.dragData, e);
18348     },
18349
18350     // private
18351     onEndDrag : function(data, e){
18352     },
18353     
18354     // private - pin to cursor
18355     autoOffset : function(x, y) {
18356         this.setDelta(-12, -20);
18357     }    
18358 });/*
18359  * Based on:
18360  * Ext JS Library 1.1.1
18361  * Copyright(c) 2006-2007, Ext JS, LLC.
18362  *
18363  * Originally Released Under LGPL - original licence link has changed is not relivant.
18364  *
18365  * Fork - LGPL
18366  * <script type="text/javascript">
18367  */
18368
18369
18370 /**
18371  * @class Roo.dd.DropTarget
18372  * @extends Roo.dd.DDTarget
18373  * A simple class that provides the basic implementation needed to make any element a drop target that can have
18374  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
18375  * @constructor
18376  * @param {String/HTMLElement/Element} el The container element
18377  * @param {Object} config
18378  */
18379 Roo.dd.DropTarget = function(el, config){
18380     this.el = Roo.get(el);
18381     
18382     var listeners = false; ;
18383     if (config && config.listeners) {
18384         listeners= config.listeners;
18385         delete config.listeners;
18386     }
18387     Roo.apply(this, config);
18388     
18389     if(this.containerScroll){
18390         Roo.dd.ScrollManager.register(this.el);
18391     }
18392     this.addEvents( {
18393          /**
18394          * @scope Roo.dd.DropTarget
18395          */
18396          
18397          /**
18398          * @event enter
18399          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
18400          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
18401          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
18402          * 
18403          * IMPORTANT : it should set this.overClass and this.dropAllowed
18404          * 
18405          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18406          * @param {Event} e The event
18407          * @param {Object} data An object containing arbitrary data supplied by the drag source
18408          */
18409         "enter" : true,
18410         
18411          /**
18412          * @event over
18413          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
18414          * This method will be called on every mouse movement while the drag source is over the drop target.
18415          * This default implementation simply returns the dropAllowed config value.
18416          * 
18417          * IMPORTANT : it should set this.dropAllowed
18418          * 
18419          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18420          * @param {Event} e The event
18421          * @param {Object} data An object containing arbitrary data supplied by the drag source
18422          
18423          */
18424         "over" : true,
18425         /**
18426          * @event out
18427          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
18428          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
18429          * overClass (if any) from the drop element.
18430          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18431          * @param {Event} e The event
18432          * @param {Object} data An object containing arbitrary data supplied by the drag source
18433          */
18434          "out" : true,
18435          
18436         /**
18437          * @event drop
18438          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
18439          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
18440          * implementation that does something to process the drop event and returns true so that the drag source's
18441          * repair action does not run.
18442          * 
18443          * IMPORTANT : it should set this.success
18444          * 
18445          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18446          * @param {Event} e The event
18447          * @param {Object} data An object containing arbitrary data supplied by the drag source
18448         */
18449          "drop" : true
18450     });
18451             
18452      
18453     Roo.dd.DropTarget.superclass.constructor.call(  this, 
18454         this.el.dom, 
18455         this.ddGroup || this.group,
18456         {
18457             isTarget: true,
18458             listeners : listeners || {} 
18459            
18460         
18461         }
18462     );
18463
18464 };
18465
18466 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
18467     /**
18468      * @cfg {String} overClass
18469      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
18470      */
18471      /**
18472      * @cfg {String} ddGroup
18473      * The drag drop group to handle drop events for
18474      */
18475      
18476     /**
18477      * @cfg {String} dropAllowed
18478      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
18479      */
18480     dropAllowed : "x-dd-drop-ok",
18481     /**
18482      * @cfg {String} dropNotAllowed
18483      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
18484      */
18485     dropNotAllowed : "x-dd-drop-nodrop",
18486     /**
18487      * @cfg {boolean} success
18488      * set this after drop listener.. 
18489      */
18490     success : false,
18491     /**
18492      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
18493      * if the drop point is valid for over/enter..
18494      */
18495     valid : false,
18496     // private
18497     isTarget : true,
18498
18499     // private
18500     isNotifyTarget : true,
18501     
18502     /**
18503      * @hide
18504      */
18505     notifyEnter : function(dd, e, data)
18506     {
18507         this.valid = true;
18508         this.fireEvent('enter', dd, e, data);
18509         if(this.overClass){
18510             this.el.addClass(this.overClass);
18511         }
18512         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
18513             this.valid ? this.dropAllowed : this.dropNotAllowed
18514         );
18515     },
18516
18517     /**
18518      * @hide
18519      */
18520     notifyOver : function(dd, e, data)
18521     {
18522         this.valid = true;
18523         this.fireEvent('over', dd, e, data);
18524         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
18525             this.valid ? this.dropAllowed : this.dropNotAllowed
18526         );
18527     },
18528
18529     /**
18530      * @hide
18531      */
18532     notifyOut : function(dd, e, data)
18533     {
18534         this.fireEvent('out', dd, e, data);
18535         if(this.overClass){
18536             this.el.removeClass(this.overClass);
18537         }
18538     },
18539
18540     /**
18541      * @hide
18542      */
18543     notifyDrop : function(dd, e, data)
18544     {
18545         this.success = false;
18546         this.fireEvent('drop', dd, e, data);
18547         return this.success;
18548     }
18549 });/*
18550  * Based on:
18551  * Ext JS Library 1.1.1
18552  * Copyright(c) 2006-2007, Ext JS, LLC.
18553  *
18554  * Originally Released Under LGPL - original licence link has changed is not relivant.
18555  *
18556  * Fork - LGPL
18557  * <script type="text/javascript">
18558  */
18559
18560
18561 /**
18562  * @class Roo.dd.DragZone
18563  * @extends Roo.dd.DragSource
18564  * This class provides a container DD instance that proxies for multiple child node sources.<br />
18565  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
18566  * @constructor
18567  * @param {String/HTMLElement/Element} el The container element
18568  * @param {Object} config
18569  */
18570 Roo.dd.DragZone = function(el, config){
18571     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
18572     if(this.containerScroll){
18573         Roo.dd.ScrollManager.register(this.el);
18574     }
18575 };
18576
18577 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
18578     /**
18579      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
18580      * for auto scrolling during drag operations.
18581      */
18582     /**
18583      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
18584      * method after a failed drop (defaults to "c3daf9" - light blue)
18585      */
18586
18587     /**
18588      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
18589      * for a valid target to drag based on the mouse down. Override this method
18590      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
18591      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
18592      * @param {EventObject} e The mouse down event
18593      * @return {Object} The dragData
18594      */
18595     getDragData : function(e){
18596         return Roo.dd.Registry.getHandleFromEvent(e);
18597     },
18598     
18599     /**
18600      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
18601      * this.dragData.ddel
18602      * @param {Number} x The x position of the click on the dragged object
18603      * @param {Number} y The y position of the click on the dragged object
18604      * @return {Boolean} true to continue the drag, false to cancel
18605      */
18606     onInitDrag : function(x, y){
18607         this.proxy.update(this.dragData.ddel.cloneNode(true));
18608         this.onStartDrag(x, y);
18609         return true;
18610     },
18611     
18612     /**
18613      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
18614      */
18615     afterRepair : function(){
18616         if(Roo.enableFx){
18617             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
18618         }
18619         this.dragging = false;
18620     },
18621
18622     /**
18623      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
18624      * the XY of this.dragData.ddel
18625      * @param {EventObject} e The mouse up event
18626      * @return {Array} The xy location (e.g. [100, 200])
18627      */
18628     getRepairXY : function(e){
18629         return Roo.Element.fly(this.dragData.ddel).getXY();  
18630     }
18631 });/*
18632  * Based on:
18633  * Ext JS Library 1.1.1
18634  * Copyright(c) 2006-2007, Ext JS, LLC.
18635  *
18636  * Originally Released Under LGPL - original licence link has changed is not relivant.
18637  *
18638  * Fork - LGPL
18639  * <script type="text/javascript">
18640  */
18641 /**
18642  * @class Roo.dd.DropZone
18643  * @extends Roo.dd.DropTarget
18644  * This class provides a container DD instance that proxies for multiple child node targets.<br />
18645  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
18646  * @constructor
18647  * @param {String/HTMLElement/Element} el The container element
18648  * @param {Object} config
18649  */
18650 Roo.dd.DropZone = function(el, config){
18651     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
18652 };
18653
18654 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
18655     /**
18656      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
18657      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
18658      * provide your own custom lookup.
18659      * @param {Event} e The event
18660      * @return {Object} data The custom data
18661      */
18662     getTargetFromEvent : function(e){
18663         return Roo.dd.Registry.getTargetFromEvent(e);
18664     },
18665
18666     /**
18667      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
18668      * that it has registered.  This method has no default implementation and should be overridden to provide
18669      * node-specific processing if necessary.
18670      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
18671      * {@link #getTargetFromEvent} for this node)
18672      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18673      * @param {Event} e The event
18674      * @param {Object} data An object containing arbitrary data supplied by the drag source
18675      */
18676     onNodeEnter : function(n, dd, e, data){
18677         
18678     },
18679
18680     /**
18681      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
18682      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
18683      * overridden to provide the proper feedback.
18684      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18685      * {@link #getTargetFromEvent} for this node)
18686      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18687      * @param {Event} e The event
18688      * @param {Object} data An object containing arbitrary data supplied by the drag source
18689      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18690      * underlying {@link Roo.dd.StatusProxy} can be updated
18691      */
18692     onNodeOver : function(n, dd, e, data){
18693         return this.dropAllowed;
18694     },
18695
18696     /**
18697      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
18698      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
18699      * node-specific processing if necessary.
18700      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18701      * {@link #getTargetFromEvent} for this node)
18702      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18703      * @param {Event} e The event
18704      * @param {Object} data An object containing arbitrary data supplied by the drag source
18705      */
18706     onNodeOut : function(n, dd, e, data){
18707         
18708     },
18709
18710     /**
18711      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
18712      * the drop node.  The default implementation returns false, so it should be overridden to provide the
18713      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
18714      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18715      * {@link #getTargetFromEvent} for this node)
18716      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18717      * @param {Event} e The event
18718      * @param {Object} data An object containing arbitrary data supplied by the drag source
18719      * @return {Boolean} True if the drop was valid, else false
18720      */
18721     onNodeDrop : function(n, dd, e, data){
18722         return false;
18723     },
18724
18725     /**
18726      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
18727      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
18728      * it should be overridden to provide the proper feedback if necessary.
18729      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18730      * @param {Event} e The event
18731      * @param {Object} data An object containing arbitrary data supplied by the drag source
18732      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18733      * underlying {@link Roo.dd.StatusProxy} can be updated
18734      */
18735     onContainerOver : function(dd, e, data){
18736         return this.dropNotAllowed;
18737     },
18738
18739     /**
18740      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
18741      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
18742      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
18743      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
18744      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18745      * @param {Event} e The event
18746      * @param {Object} data An object containing arbitrary data supplied by the drag source
18747      * @return {Boolean} True if the drop was valid, else false
18748      */
18749     onContainerDrop : function(dd, e, data){
18750         return false;
18751     },
18752
18753     /**
18754      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
18755      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
18756      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
18757      * you should override this method and provide a custom implementation.
18758      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18759      * @param {Event} e The event
18760      * @param {Object} data An object containing arbitrary data supplied by the drag source
18761      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18762      * underlying {@link Roo.dd.StatusProxy} can be updated
18763      */
18764     notifyEnter : function(dd, e, data){
18765         return this.dropNotAllowed;
18766     },
18767
18768     /**
18769      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
18770      * This method will be called on every mouse movement while the drag source is over the drop zone.
18771      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
18772      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
18773      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
18774      * registered node, it will call {@link #onContainerOver}.
18775      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18776      * @param {Event} e The event
18777      * @param {Object} data An object containing arbitrary data supplied by the drag source
18778      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18779      * underlying {@link Roo.dd.StatusProxy} can be updated
18780      */
18781     notifyOver : function(dd, e, data){
18782         var n = this.getTargetFromEvent(e);
18783         if(!n){ // not over valid drop target
18784             if(this.lastOverNode){
18785                 this.onNodeOut(this.lastOverNode, dd, e, data);
18786                 this.lastOverNode = null;
18787             }
18788             return this.onContainerOver(dd, e, data);
18789         }
18790         if(this.lastOverNode != n){
18791             if(this.lastOverNode){
18792                 this.onNodeOut(this.lastOverNode, dd, e, data);
18793             }
18794             this.onNodeEnter(n, dd, e, data);
18795             this.lastOverNode = n;
18796         }
18797         return this.onNodeOver(n, dd, e, data);
18798     },
18799
18800     /**
18801      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
18802      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
18803      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
18804      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18805      * @param {Event} e The event
18806      * @param {Object} data An object containing arbitrary data supplied by the drag zone
18807      */
18808     notifyOut : function(dd, e, data){
18809         if(this.lastOverNode){
18810             this.onNodeOut(this.lastOverNode, dd, e, data);
18811             this.lastOverNode = null;
18812         }
18813     },
18814
18815     /**
18816      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
18817      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
18818      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
18819      * otherwise it will call {@link #onContainerDrop}.
18820      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18821      * @param {Event} e The event
18822      * @param {Object} data An object containing arbitrary data supplied by the drag source
18823      * @return {Boolean} True if the drop was valid, else false
18824      */
18825     notifyDrop : function(dd, e, data){
18826         if(this.lastOverNode){
18827             this.onNodeOut(this.lastOverNode, dd, e, data);
18828             this.lastOverNode = null;
18829         }
18830         var n = this.getTargetFromEvent(e);
18831         return n ?
18832             this.onNodeDrop(n, dd, e, data) :
18833             this.onContainerDrop(dd, e, data);
18834     },
18835
18836     // private
18837     triggerCacheRefresh : function(){
18838         Roo.dd.DDM.refreshCache(this.groups);
18839     }  
18840 });/*
18841  * Based on:
18842  * Ext JS Library 1.1.1
18843  * Copyright(c) 2006-2007, Ext JS, LLC.
18844  *
18845  * Originally Released Under LGPL - original licence link has changed is not relivant.
18846  *
18847  * Fork - LGPL
18848  * <script type="text/javascript">
18849  */
18850
18851
18852 /**
18853  * @class Roo.data.SortTypes
18854  * @singleton
18855  * Defines the default sorting (casting?) comparison functions used when sorting data.
18856  */
18857 Roo.data.SortTypes = {
18858     /**
18859      * Default sort that does nothing
18860      * @param {Mixed} s The value being converted
18861      * @return {Mixed} The comparison value
18862      */
18863     none : function(s){
18864         return s;
18865     },
18866     
18867     /**
18868      * The regular expression used to strip tags
18869      * @type {RegExp}
18870      * @property
18871      */
18872     stripTagsRE : /<\/?[^>]+>/gi,
18873     
18874     /**
18875      * Strips all HTML tags to sort on text only
18876      * @param {Mixed} s The value being converted
18877      * @return {String} The comparison value
18878      */
18879     asText : function(s){
18880         return String(s).replace(this.stripTagsRE, "");
18881     },
18882     
18883     /**
18884      * Strips all HTML tags to sort on text only - Case insensitive
18885      * @param {Mixed} s The value being converted
18886      * @return {String} The comparison value
18887      */
18888     asUCText : function(s){
18889         return String(s).toUpperCase().replace(this.stripTagsRE, "");
18890     },
18891     
18892     /**
18893      * Case insensitive string
18894      * @param {Mixed} s The value being converted
18895      * @return {String} The comparison value
18896      */
18897     asUCString : function(s) {
18898         return String(s).toUpperCase();
18899     },
18900     
18901     /**
18902      * Date sorting
18903      * @param {Mixed} s The value being converted
18904      * @return {Number} The comparison value
18905      */
18906     asDate : function(s) {
18907         if(!s){
18908             return 0;
18909         }
18910         if(s instanceof Date){
18911             return s.getTime();
18912         }
18913         return Date.parse(String(s));
18914     },
18915     
18916     /**
18917      * Float sorting
18918      * @param {Mixed} s The value being converted
18919      * @return {Float} The comparison value
18920      */
18921     asFloat : function(s) {
18922         var val = parseFloat(String(s).replace(/,/g, ""));
18923         if(isNaN(val)) val = 0;
18924         return val;
18925     },
18926     
18927     /**
18928      * Integer sorting
18929      * @param {Mixed} s The value being converted
18930      * @return {Number} The comparison value
18931      */
18932     asInt : function(s) {
18933         var val = parseInt(String(s).replace(/,/g, ""));
18934         if(isNaN(val)) val = 0;
18935         return val;
18936     }
18937 };/*
18938  * Based on:
18939  * Ext JS Library 1.1.1
18940  * Copyright(c) 2006-2007, Ext JS, LLC.
18941  *
18942  * Originally Released Under LGPL - original licence link has changed is not relivant.
18943  *
18944  * Fork - LGPL
18945  * <script type="text/javascript">
18946  */
18947
18948 /**
18949 * @class Roo.data.Record
18950  * Instances of this class encapsulate both record <em>definition</em> information, and record
18951  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
18952  * to access Records cached in an {@link Roo.data.Store} object.<br>
18953  * <p>
18954  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
18955  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
18956  * objects.<br>
18957  * <p>
18958  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
18959  * @constructor
18960  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
18961  * {@link #create}. The parameters are the same.
18962  * @param {Array} data An associative Array of data values keyed by the field name.
18963  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
18964  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
18965  * not specified an integer id is generated.
18966  */
18967 Roo.data.Record = function(data, id){
18968     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
18969     this.data = data;
18970 };
18971
18972 /**
18973  * Generate a constructor for a specific record layout.
18974  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
18975  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
18976  * Each field definition object may contain the following properties: <ul>
18977  * <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,
18978  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
18979  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
18980  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
18981  * is being used, then this is a string containing the javascript expression to reference the data relative to 
18982  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
18983  * to the data item relative to the record element. If the mapping expression is the same as the field name,
18984  * this may be omitted.</p></li>
18985  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
18986  * <ul><li>auto (Default, implies no conversion)</li>
18987  * <li>string</li>
18988  * <li>int</li>
18989  * <li>float</li>
18990  * <li>boolean</li>
18991  * <li>date</li></ul></p></li>
18992  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
18993  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
18994  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
18995  * by the Reader into an object that will be stored in the Record. It is passed the
18996  * following parameters:<ul>
18997  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
18998  * </ul></p></li>
18999  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
19000  * </ul>
19001  * <br>usage:<br><pre><code>
19002 var TopicRecord = Roo.data.Record.create(
19003     {name: 'title', mapping: 'topic_title'},
19004     {name: 'author', mapping: 'username'},
19005     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
19006     {name: 'lastPost', mapping: 'post_time', type: 'date'},
19007     {name: 'lastPoster', mapping: 'user2'},
19008     {name: 'excerpt', mapping: 'post_text'}
19009 );
19010
19011 var myNewRecord = new TopicRecord({
19012     title: 'Do my job please',
19013     author: 'noobie',
19014     totalPosts: 1,
19015     lastPost: new Date(),
19016     lastPoster: 'Animal',
19017     excerpt: 'No way dude!'
19018 });
19019 myStore.add(myNewRecord);
19020 </code></pre>
19021  * @method create
19022  * @static
19023  */
19024 Roo.data.Record.create = function(o){
19025     var f = function(){
19026         f.superclass.constructor.apply(this, arguments);
19027     };
19028     Roo.extend(f, Roo.data.Record);
19029     var p = f.prototype;
19030     p.fields = new Roo.util.MixedCollection(false, function(field){
19031         return field.name;
19032     });
19033     for(var i = 0, len = o.length; i < len; i++){
19034         p.fields.add(new Roo.data.Field(o[i]));
19035     }
19036     f.getField = function(name){
19037         return p.fields.get(name);  
19038     };
19039     return f;
19040 };
19041
19042 Roo.data.Record.AUTO_ID = 1000;
19043 Roo.data.Record.EDIT = 'edit';
19044 Roo.data.Record.REJECT = 'reject';
19045 Roo.data.Record.COMMIT = 'commit';
19046
19047 Roo.data.Record.prototype = {
19048     /**
19049      * Readonly flag - true if this record has been modified.
19050      * @type Boolean
19051      */
19052     dirty : false,
19053     editing : false,
19054     error: null,
19055     modified: null,
19056
19057     // private
19058     join : function(store){
19059         this.store = store;
19060     },
19061
19062     /**
19063      * Set the named field to the specified value.
19064      * @param {String} name The name of the field to set.
19065      * @param {Object} value The value to set the field to.
19066      */
19067     set : function(name, value){
19068         if(this.data[name] == value){
19069             return;
19070         }
19071         this.dirty = true;
19072         if(!this.modified){
19073             this.modified = {};
19074         }
19075         if(typeof this.modified[name] == 'undefined'){
19076             this.modified[name] = this.data[name];
19077         }
19078         this.data[name] = value;
19079         if(!this.editing && this.store){
19080             this.store.afterEdit(this);
19081         }       
19082     },
19083
19084     /**
19085      * Get the value of the named field.
19086      * @param {String} name The name of the field to get the value of.
19087      * @return {Object} The value of the field.
19088      */
19089     get : function(name){
19090         return this.data[name]; 
19091     },
19092
19093     // private
19094     beginEdit : function(){
19095         this.editing = true;
19096         this.modified = {}; 
19097     },
19098
19099     // private
19100     cancelEdit : function(){
19101         this.editing = false;
19102         delete this.modified;
19103     },
19104
19105     // private
19106     endEdit : function(){
19107         this.editing = false;
19108         if(this.dirty && this.store){
19109             this.store.afterEdit(this);
19110         }
19111     },
19112
19113     /**
19114      * Usually called by the {@link Roo.data.Store} which owns the Record.
19115      * Rejects all changes made to the Record since either creation, or the last commit operation.
19116      * Modified fields are reverted to their original values.
19117      * <p>
19118      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
19119      * of reject operations.
19120      */
19121     reject : function(){
19122         var m = this.modified;
19123         for(var n in m){
19124             if(typeof m[n] != "function"){
19125                 this.data[n] = m[n];
19126             }
19127         }
19128         this.dirty = false;
19129         delete this.modified;
19130         this.editing = false;
19131         if(this.store){
19132             this.store.afterReject(this);
19133         }
19134     },
19135
19136     /**
19137      * Usually called by the {@link Roo.data.Store} which owns the Record.
19138      * Commits all changes made to the Record since either creation, or the last commit operation.
19139      * <p>
19140      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
19141      * of commit operations.
19142      */
19143     commit : function(){
19144         this.dirty = false;
19145         delete this.modified;
19146         this.editing = false;
19147         if(this.store){
19148             this.store.afterCommit(this);
19149         }
19150     },
19151
19152     // private
19153     hasError : function(){
19154         return this.error != null;
19155     },
19156
19157     // private
19158     clearError : function(){
19159         this.error = null;
19160     },
19161
19162     /**
19163      * Creates a copy of this record.
19164      * @param {String} id (optional) A new record id if you don't want to use this record's id
19165      * @return {Record}
19166      */
19167     copy : function(newId) {
19168         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
19169     }
19170 };/*
19171  * Based on:
19172  * Ext JS Library 1.1.1
19173  * Copyright(c) 2006-2007, Ext JS, LLC.
19174  *
19175  * Originally Released Under LGPL - original licence link has changed is not relivant.
19176  *
19177  * Fork - LGPL
19178  * <script type="text/javascript">
19179  */
19180
19181
19182
19183 /**
19184  * @class Roo.data.Store
19185  * @extends Roo.util.Observable
19186  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
19187  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
19188  * <p>
19189  * 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
19190  * has no knowledge of the format of the data returned by the Proxy.<br>
19191  * <p>
19192  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
19193  * instances from the data object. These records are cached and made available through accessor functions.
19194  * @constructor
19195  * Creates a new Store.
19196  * @param {Object} config A config object containing the objects needed for the Store to access data,
19197  * and read the data into Records.
19198  */
19199 Roo.data.Store = function(config){
19200     this.data = new Roo.util.MixedCollection(false);
19201     this.data.getKey = function(o){
19202         return o.id;
19203     };
19204     this.baseParams = {};
19205     // private
19206     this.paramNames = {
19207         "start" : "start",
19208         "limit" : "limit",
19209         "sort" : "sort",
19210         "dir" : "dir",
19211         "multisort" : "_multisort"
19212     };
19213
19214     if(config && config.data){
19215         this.inlineData = config.data;
19216         delete config.data;
19217     }
19218
19219     Roo.apply(this, config);
19220     
19221     if(this.reader){ // reader passed
19222         this.reader = Roo.factory(this.reader, Roo.data);
19223         this.reader.xmodule = this.xmodule || false;
19224         if(!this.recordType){
19225             this.recordType = this.reader.recordType;
19226         }
19227         if(this.reader.onMetaChange){
19228             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
19229         }
19230     }
19231
19232     if(this.recordType){
19233         this.fields = this.recordType.prototype.fields;
19234     }
19235     this.modified = [];
19236
19237     this.addEvents({
19238         /**
19239          * @event datachanged
19240          * Fires when the data cache has changed, and a widget which is using this Store
19241          * as a Record cache should refresh its view.
19242          * @param {Store} this
19243          */
19244         datachanged : true,
19245         /**
19246          * @event metachange
19247          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
19248          * @param {Store} this
19249          * @param {Object} meta The JSON metadata
19250          */
19251         metachange : true,
19252         /**
19253          * @event add
19254          * Fires when Records have been added to the Store
19255          * @param {Store} this
19256          * @param {Roo.data.Record[]} records The array of Records added
19257          * @param {Number} index The index at which the record(s) were added
19258          */
19259         add : true,
19260         /**
19261          * @event remove
19262          * Fires when a Record has been removed from the Store
19263          * @param {Store} this
19264          * @param {Roo.data.Record} record The Record that was removed
19265          * @param {Number} index The index at which the record was removed
19266          */
19267         remove : true,
19268         /**
19269          * @event update
19270          * Fires when a Record has been updated
19271          * @param {Store} this
19272          * @param {Roo.data.Record} record The Record that was updated
19273          * @param {String} operation The update operation being performed.  Value may be one of:
19274          * <pre><code>
19275  Roo.data.Record.EDIT
19276  Roo.data.Record.REJECT
19277  Roo.data.Record.COMMIT
19278          * </code></pre>
19279          */
19280         update : true,
19281         /**
19282          * @event clear
19283          * Fires when the data cache has been cleared.
19284          * @param {Store} this
19285          */
19286         clear : true,
19287         /**
19288          * @event beforeload
19289          * Fires before a request is made for a new data object.  If the beforeload handler returns false
19290          * the load action will be canceled.
19291          * @param {Store} this
19292          * @param {Object} options The loading options that were specified (see {@link #load} for details)
19293          */
19294         beforeload : true,
19295         /**
19296          * @event load
19297          * Fires after a new set of Records has been loaded.
19298          * @param {Store} this
19299          * @param {Roo.data.Record[]} records The Records that were loaded
19300          * @param {Object} options The loading options that were specified (see {@link #load} for details)
19301          */
19302         load : true,
19303         /**
19304          * @event loadexception
19305          * Fires if an exception occurs in the Proxy during loading.
19306          * Called with the signature of the Proxy's "loadexception" event.
19307          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
19308          * 
19309          * @param {Proxy} 
19310          * @param {Object} return from JsonData.reader() - success, totalRecords, records
19311          * @param {Object} load options 
19312          * @param {Object} jsonData from your request (normally this contains the Exception)
19313          */
19314         loadexception : true
19315     });
19316     
19317     if(this.proxy){
19318         this.proxy = Roo.factory(this.proxy, Roo.data);
19319         this.proxy.xmodule = this.xmodule || false;
19320         this.relayEvents(this.proxy,  ["loadexception"]);
19321     }
19322     this.sortToggle = {};
19323     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
19324
19325     Roo.data.Store.superclass.constructor.call(this);
19326
19327     if(this.inlineData){
19328         this.loadData(this.inlineData);
19329         delete this.inlineData;
19330     }
19331 };
19332 Roo.extend(Roo.data.Store, Roo.util.Observable, {
19333      /**
19334     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
19335     * without a remote query - used by combo/forms at present.
19336     */
19337     
19338     /**
19339     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
19340     */
19341     /**
19342     * @cfg {Array} data Inline data to be loaded when the store is initialized.
19343     */
19344     /**
19345     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
19346     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
19347     */
19348     /**
19349     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
19350     * on any HTTP request
19351     */
19352     /**
19353     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
19354     */
19355     /**
19356     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
19357     */
19358     multiSort: false,
19359     /**
19360     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
19361     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
19362     */
19363     remoteSort : false,
19364
19365     /**
19366     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
19367      * loaded or when a record is removed. (defaults to false).
19368     */
19369     pruneModifiedRecords : false,
19370
19371     // private
19372     lastOptions : null,
19373
19374     /**
19375      * Add Records to the Store and fires the add event.
19376      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19377      */
19378     add : function(records){
19379         records = [].concat(records);
19380         for(var i = 0, len = records.length; i < len; i++){
19381             records[i].join(this);
19382         }
19383         var index = this.data.length;
19384         this.data.addAll(records);
19385         this.fireEvent("add", this, records, index);
19386     },
19387
19388     /**
19389      * Remove a Record from the Store and fires the remove event.
19390      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
19391      */
19392     remove : function(record){
19393         var index = this.data.indexOf(record);
19394         this.data.removeAt(index);
19395         if(this.pruneModifiedRecords){
19396             this.modified.remove(record);
19397         }
19398         this.fireEvent("remove", this, record, index);
19399     },
19400
19401     /**
19402      * Remove all Records from the Store and fires the clear event.
19403      */
19404     removeAll : function(){
19405         this.data.clear();
19406         if(this.pruneModifiedRecords){
19407             this.modified = [];
19408         }
19409         this.fireEvent("clear", this);
19410     },
19411
19412     /**
19413      * Inserts Records to the Store at the given index and fires the add event.
19414      * @param {Number} index The start index at which to insert the passed Records.
19415      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19416      */
19417     insert : function(index, records){
19418         records = [].concat(records);
19419         for(var i = 0, len = records.length; i < len; i++){
19420             this.data.insert(index, records[i]);
19421             records[i].join(this);
19422         }
19423         this.fireEvent("add", this, records, index);
19424     },
19425
19426     /**
19427      * Get the index within the cache of the passed Record.
19428      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
19429      * @return {Number} The index of the passed Record. Returns -1 if not found.
19430      */
19431     indexOf : function(record){
19432         return this.data.indexOf(record);
19433     },
19434
19435     /**
19436      * Get the index within the cache of the Record with the passed id.
19437      * @param {String} id The id of the Record to find.
19438      * @return {Number} The index of the Record. Returns -1 if not found.
19439      */
19440     indexOfId : function(id){
19441         return this.data.indexOfKey(id);
19442     },
19443
19444     /**
19445      * Get the Record with the specified id.
19446      * @param {String} id The id of the Record to find.
19447      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
19448      */
19449     getById : function(id){
19450         return this.data.key(id);
19451     },
19452
19453     /**
19454      * Get the Record at the specified index.
19455      * @param {Number} index The index of the Record to find.
19456      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
19457      */
19458     getAt : function(index){
19459         return this.data.itemAt(index);
19460     },
19461
19462     /**
19463      * Returns a range of Records between specified indices.
19464      * @param {Number} startIndex (optional) The starting index (defaults to 0)
19465      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
19466      * @return {Roo.data.Record[]} An array of Records
19467      */
19468     getRange : function(start, end){
19469         return this.data.getRange(start, end);
19470     },
19471
19472     // private
19473     storeOptions : function(o){
19474         o = Roo.apply({}, o);
19475         delete o.callback;
19476         delete o.scope;
19477         this.lastOptions = o;
19478     },
19479
19480     /**
19481      * Loads the Record cache from the configured Proxy using the configured Reader.
19482      * <p>
19483      * If using remote paging, then the first load call must specify the <em>start</em>
19484      * and <em>limit</em> properties in the options.params property to establish the initial
19485      * position within the dataset, and the number of Records to cache on each read from the Proxy.
19486      * <p>
19487      * <strong>It is important to note that for remote data sources, loading is asynchronous,
19488      * and this call will return before the new data has been loaded. Perform any post-processing
19489      * in a callback function, or in a "load" event handler.</strong>
19490      * <p>
19491      * @param {Object} options An object containing properties which control loading options:<ul>
19492      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
19493      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
19494      * passed the following arguments:<ul>
19495      * <li>r : Roo.data.Record[]</li>
19496      * <li>options: Options object from the load call</li>
19497      * <li>success: Boolean success indicator</li></ul></li>
19498      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
19499      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
19500      * </ul>
19501      */
19502     load : function(options){
19503         options = options || {};
19504         if(this.fireEvent("beforeload", this, options) !== false){
19505             this.storeOptions(options);
19506             var p = Roo.apply(options.params || {}, this.baseParams);
19507             // if meta was not loaded from remote source.. try requesting it.
19508             if (!this.reader.metaFromRemote) {
19509                 p._requestMeta = 1;
19510             }
19511             if(this.sortInfo && this.remoteSort){
19512                 var pn = this.paramNames;
19513                 p[pn["sort"]] = this.sortInfo.field;
19514                 p[pn["dir"]] = this.sortInfo.direction;
19515             }
19516             if (this.multiSort) {
19517                 var pn = this.paramNames;
19518                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
19519             }
19520             
19521             this.proxy.load(p, this.reader, this.loadRecords, this, options);
19522         }
19523     },
19524
19525     /**
19526      * Reloads the Record cache from the configured Proxy using the configured Reader and
19527      * the options from the last load operation performed.
19528      * @param {Object} options (optional) An object containing properties which may override the options
19529      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
19530      * the most recently used options are reused).
19531      */
19532     reload : function(options){
19533         this.load(Roo.applyIf(options||{}, this.lastOptions));
19534     },
19535
19536     // private
19537     // Called as a callback by the Reader during a load operation.
19538     loadRecords : function(o, options, success){
19539         if(!o || success === false){
19540             if(success !== false){
19541                 this.fireEvent("load", this, [], options);
19542             }
19543             if(options.callback){
19544                 options.callback.call(options.scope || this, [], options, false);
19545             }
19546             return;
19547         }
19548         // if data returned failure - throw an exception.
19549         if (o.success === false) {
19550             this.fireEvent("loadexception", this, o, options, this.reader.jsonData);
19551             return;
19552         }
19553         var r = o.records, t = o.totalRecords || r.length;
19554         if(!options || options.add !== true){
19555             if(this.pruneModifiedRecords){
19556                 this.modified = [];
19557             }
19558             for(var i = 0, len = r.length; i < len; i++){
19559                 r[i].join(this);
19560             }
19561             if(this.snapshot){
19562                 this.data = this.snapshot;
19563                 delete this.snapshot;
19564             }
19565             this.data.clear();
19566             this.data.addAll(r);
19567             this.totalLength = t;
19568             this.applySort();
19569             this.fireEvent("datachanged", this);
19570         }else{
19571             this.totalLength = Math.max(t, this.data.length+r.length);
19572             this.add(r);
19573         }
19574         this.fireEvent("load", this, r, options);
19575         if(options.callback){
19576             options.callback.call(options.scope || this, r, options, true);
19577         }
19578     },
19579
19580     /**
19581      * Loads data from a passed data block. A Reader which understands the format of the data
19582      * must have been configured in the constructor.
19583      * @param {Object} data The data block from which to read the Records.  The format of the data expected
19584      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
19585      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
19586      */
19587     loadData : function(o, append){
19588         var r = this.reader.readRecords(o);
19589         this.loadRecords(r, {add: append}, true);
19590     },
19591
19592     /**
19593      * Gets the number of cached records.
19594      * <p>
19595      * <em>If using paging, this may not be the total size of the dataset. If the data object
19596      * used by the Reader contains the dataset size, then the getTotalCount() function returns
19597      * the data set size</em>
19598      */
19599     getCount : function(){
19600         return this.data.length || 0;
19601     },
19602
19603     /**
19604      * Gets the total number of records in the dataset as returned by the server.
19605      * <p>
19606      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
19607      * the dataset size</em>
19608      */
19609     getTotalCount : function(){
19610         return this.totalLength || 0;
19611     },
19612
19613     /**
19614      * Returns the sort state of the Store as an object with two properties:
19615      * <pre><code>
19616  field {String} The name of the field by which the Records are sorted
19617  direction {String} The sort order, "ASC" or "DESC"
19618      * </code></pre>
19619      */
19620     getSortState : function(){
19621         return this.sortInfo;
19622     },
19623
19624     // private
19625     applySort : function(){
19626         if(this.sortInfo && !this.remoteSort){
19627             var s = this.sortInfo, f = s.field;
19628             var st = this.fields.get(f).sortType;
19629             var fn = function(r1, r2){
19630                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
19631                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
19632             };
19633             this.data.sort(s.direction, fn);
19634             if(this.snapshot && this.snapshot != this.data){
19635                 this.snapshot.sort(s.direction, fn);
19636             }
19637         }
19638     },
19639
19640     /**
19641      * Sets the default sort column and order to be used by the next load operation.
19642      * @param {String} fieldName The name of the field to sort by.
19643      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
19644      */
19645     setDefaultSort : function(field, dir){
19646         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
19647     },
19648
19649     /**
19650      * Sort the Records.
19651      * If remote sorting is used, the sort is performed on the server, and the cache is
19652      * reloaded. If local sorting is used, the cache is sorted internally.
19653      * @param {String} fieldName The name of the field to sort by.
19654      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
19655      */
19656     sort : function(fieldName, dir){
19657         var f = this.fields.get(fieldName);
19658         if(!dir){
19659             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
19660             
19661             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
19662                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
19663             }else{
19664                 dir = f.sortDir;
19665             }
19666         }
19667         this.sortToggle[f.name] = dir;
19668         this.sortInfo = {field: f.name, direction: dir};
19669         if(!this.remoteSort){
19670             this.applySort();
19671             this.fireEvent("datachanged", this);
19672         }else{
19673             this.load(this.lastOptions);
19674         }
19675     },
19676
19677     /**
19678      * Calls the specified function for each of the Records in the cache.
19679      * @param {Function} fn The function to call. The Record is passed as the first parameter.
19680      * Returning <em>false</em> aborts and exits the iteration.
19681      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
19682      */
19683     each : function(fn, scope){
19684         this.data.each(fn, scope);
19685     },
19686
19687     /**
19688      * Gets all records modified since the last commit.  Modified records are persisted across load operations
19689      * (e.g., during paging).
19690      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
19691      */
19692     getModifiedRecords : function(){
19693         return this.modified;
19694     },
19695
19696     // private
19697     createFilterFn : function(property, value, anyMatch){
19698         if(!value.exec){ // not a regex
19699             value = String(value);
19700             if(value.length == 0){
19701                 return false;
19702             }
19703             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
19704         }
19705         return function(r){
19706             return value.test(r.data[property]);
19707         };
19708     },
19709
19710     /**
19711      * Sums the value of <i>property</i> for each record between start and end and returns the result.
19712      * @param {String} property A field on your records
19713      * @param {Number} start The record index to start at (defaults to 0)
19714      * @param {Number} end The last record index to include (defaults to length - 1)
19715      * @return {Number} The sum
19716      */
19717     sum : function(property, start, end){
19718         var rs = this.data.items, v = 0;
19719         start = start || 0;
19720         end = (end || end === 0) ? end : rs.length-1;
19721
19722         for(var i = start; i <= end; i++){
19723             v += (rs[i].data[property] || 0);
19724         }
19725         return v;
19726     },
19727
19728     /**
19729      * Filter the records by a specified property.
19730      * @param {String} field A field on your records
19731      * @param {String/RegExp} value Either a string that the field
19732      * should start with or a RegExp to test against the field
19733      * @param {Boolean} anyMatch True to match any part not just the beginning
19734      */
19735     filter : function(property, value, anyMatch){
19736         var fn = this.createFilterFn(property, value, anyMatch);
19737         return fn ? this.filterBy(fn) : this.clearFilter();
19738     },
19739
19740     /**
19741      * Filter by a function. The specified function will be called with each
19742      * record in this data source. If the function returns true the record is included,
19743      * otherwise it is filtered.
19744      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
19745      * @param {Object} scope (optional) The scope of the function (defaults to this)
19746      */
19747     filterBy : function(fn, scope){
19748         this.snapshot = this.snapshot || this.data;
19749         this.data = this.queryBy(fn, scope||this);
19750         this.fireEvent("datachanged", this);
19751     },
19752
19753     /**
19754      * Query the records by a specified property.
19755      * @param {String} field A field on your records
19756      * @param {String/RegExp} value Either a string that the field
19757      * should start with or a RegExp to test against the field
19758      * @param {Boolean} anyMatch True to match any part not just the beginning
19759      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
19760      */
19761     query : function(property, value, anyMatch){
19762         var fn = this.createFilterFn(property, value, anyMatch);
19763         return fn ? this.queryBy(fn) : this.data.clone();
19764     },
19765
19766     /**
19767      * Query by a function. The specified function will be called with each
19768      * record in this data source. If the function returns true the record is included
19769      * in the results.
19770      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
19771      * @param {Object} scope (optional) The scope of the function (defaults to this)
19772       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
19773      **/
19774     queryBy : function(fn, scope){
19775         var data = this.snapshot || this.data;
19776         return data.filterBy(fn, scope||this);
19777     },
19778
19779     /**
19780      * Collects unique values for a particular dataIndex from this store.
19781      * @param {String} dataIndex The property to collect
19782      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
19783      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
19784      * @return {Array} An array of the unique values
19785      **/
19786     collect : function(dataIndex, allowNull, bypassFilter){
19787         var d = (bypassFilter === true && this.snapshot) ?
19788                 this.snapshot.items : this.data.items;
19789         var v, sv, r = [], l = {};
19790         for(var i = 0, len = d.length; i < len; i++){
19791             v = d[i].data[dataIndex];
19792             sv = String(v);
19793             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
19794                 l[sv] = true;
19795                 r[r.length] = v;
19796             }
19797         }
19798         return r;
19799     },
19800
19801     /**
19802      * Revert to a view of the Record cache with no filtering applied.
19803      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
19804      */
19805     clearFilter : function(suppressEvent){
19806         if(this.snapshot && this.snapshot != this.data){
19807             this.data = this.snapshot;
19808             delete this.snapshot;
19809             if(suppressEvent !== true){
19810                 this.fireEvent("datachanged", this);
19811             }
19812         }
19813     },
19814
19815     // private
19816     afterEdit : function(record){
19817         if(this.modified.indexOf(record) == -1){
19818             this.modified.push(record);
19819         }
19820         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
19821     },
19822
19823     // private
19824     afterReject : function(record){
19825         this.modified.remove(record);
19826         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
19827     },
19828
19829     // private
19830     afterCommit : function(record){
19831         this.modified.remove(record);
19832         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
19833     },
19834
19835     /**
19836      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
19837      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
19838      */
19839     commitChanges : function(){
19840         var m = this.modified.slice(0);
19841         this.modified = [];
19842         for(var i = 0, len = m.length; i < len; i++){
19843             m[i].commit();
19844         }
19845     },
19846
19847     /**
19848      * Cancel outstanding changes on all changed records.
19849      */
19850     rejectChanges : function(){
19851         var m = this.modified.slice(0);
19852         this.modified = [];
19853         for(var i = 0, len = m.length; i < len; i++){
19854             m[i].reject();
19855         }
19856     },
19857
19858     onMetaChange : function(meta, rtype, o){
19859         this.recordType = rtype;
19860         this.fields = rtype.prototype.fields;
19861         delete this.snapshot;
19862         this.sortInfo = meta.sortInfo || this.sortInfo;
19863         this.modified = [];
19864         this.fireEvent('metachange', this, this.reader.meta);
19865     }
19866 });/*
19867  * Based on:
19868  * Ext JS Library 1.1.1
19869  * Copyright(c) 2006-2007, Ext JS, LLC.
19870  *
19871  * Originally Released Under LGPL - original licence link has changed is not relivant.
19872  *
19873  * Fork - LGPL
19874  * <script type="text/javascript">
19875  */
19876
19877 /**
19878  * @class Roo.data.SimpleStore
19879  * @extends Roo.data.Store
19880  * Small helper class to make creating Stores from Array data easier.
19881  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
19882  * @cfg {Array} fields An array of field definition objects, or field name strings.
19883  * @cfg {Array} data The multi-dimensional array of data
19884  * @constructor
19885  * @param {Object} config
19886  */
19887 Roo.data.SimpleStore = function(config){
19888     Roo.data.SimpleStore.superclass.constructor.call(this, {
19889         isLocal : true,
19890         reader: new Roo.data.ArrayReader({
19891                 id: config.id
19892             },
19893             Roo.data.Record.create(config.fields)
19894         ),
19895         proxy : new Roo.data.MemoryProxy(config.data)
19896     });
19897     this.load();
19898 };
19899 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
19900  * Based on:
19901  * Ext JS Library 1.1.1
19902  * Copyright(c) 2006-2007, Ext JS, LLC.
19903  *
19904  * Originally Released Under LGPL - original licence link has changed is not relivant.
19905  *
19906  * Fork - LGPL
19907  * <script type="text/javascript">
19908  */
19909
19910 /**
19911 /**
19912  * @extends Roo.data.Store
19913  * @class Roo.data.JsonStore
19914  * Small helper class to make creating Stores for JSON data easier. <br/>
19915 <pre><code>
19916 var store = new Roo.data.JsonStore({
19917     url: 'get-images.php',
19918     root: 'images',
19919     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
19920 });
19921 </code></pre>
19922  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
19923  * JsonReader and HttpProxy (unless inline data is provided).</b>
19924  * @cfg {Array} fields An array of field definition objects, or field name strings.
19925  * @constructor
19926  * @param {Object} config
19927  */
19928 Roo.data.JsonStore = function(c){
19929     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
19930         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
19931         reader: new Roo.data.JsonReader(c, c.fields)
19932     }));
19933 };
19934 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
19935  * Based on:
19936  * Ext JS Library 1.1.1
19937  * Copyright(c) 2006-2007, Ext JS, LLC.
19938  *
19939  * Originally Released Under LGPL - original licence link has changed is not relivant.
19940  *
19941  * Fork - LGPL
19942  * <script type="text/javascript">
19943  */
19944
19945  
19946 Roo.data.Field = function(config){
19947     if(typeof config == "string"){
19948         config = {name: config};
19949     }
19950     Roo.apply(this, config);
19951     
19952     if(!this.type){
19953         this.type = "auto";
19954     }
19955     
19956     var st = Roo.data.SortTypes;
19957     // named sortTypes are supported, here we look them up
19958     if(typeof this.sortType == "string"){
19959         this.sortType = st[this.sortType];
19960     }
19961     
19962     // set default sortType for strings and dates
19963     if(!this.sortType){
19964         switch(this.type){
19965             case "string":
19966                 this.sortType = st.asUCString;
19967                 break;
19968             case "date":
19969                 this.sortType = st.asDate;
19970                 break;
19971             default:
19972                 this.sortType = st.none;
19973         }
19974     }
19975
19976     // define once
19977     var stripRe = /[\$,%]/g;
19978
19979     // prebuilt conversion function for this field, instead of
19980     // switching every time we're reading a value
19981     if(!this.convert){
19982         var cv, dateFormat = this.dateFormat;
19983         switch(this.type){
19984             case "":
19985             case "auto":
19986             case undefined:
19987                 cv = function(v){ return v; };
19988                 break;
19989             case "string":
19990                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
19991                 break;
19992             case "int":
19993                 cv = function(v){
19994                     return v !== undefined && v !== null && v !== '' ?
19995                            parseInt(String(v).replace(stripRe, ""), 10) : '';
19996                     };
19997                 break;
19998             case "float":
19999                 cv = function(v){
20000                     return v !== undefined && v !== null && v !== '' ?
20001                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
20002                     };
20003                 break;
20004             case "bool":
20005             case "boolean":
20006                 cv = function(v){ return v === true || v === "true" || v == 1; };
20007                 break;
20008             case "date":
20009                 cv = function(v){
20010                     if(!v){
20011                         return '';
20012                     }
20013                     if(v instanceof Date){
20014                         return v;
20015                     }
20016                     if(dateFormat){
20017                         if(dateFormat == "timestamp"){
20018                             return new Date(v*1000);
20019                         }
20020                         return Date.parseDate(v, dateFormat);
20021                     }
20022                     var parsed = Date.parse(v);
20023                     return parsed ? new Date(parsed) : null;
20024                 };
20025              break;
20026             
20027         }
20028         this.convert = cv;
20029     }
20030 };
20031
20032 Roo.data.Field.prototype = {
20033     dateFormat: null,
20034     defaultValue: "",
20035     mapping: null,
20036     sortType : null,
20037     sortDir : "ASC"
20038 };/*
20039  * Based on:
20040  * Ext JS Library 1.1.1
20041  * Copyright(c) 2006-2007, Ext JS, LLC.
20042  *
20043  * Originally Released Under LGPL - original licence link has changed is not relivant.
20044  *
20045  * Fork - LGPL
20046  * <script type="text/javascript">
20047  */
20048  
20049 // Base class for reading structured data from a data source.  This class is intended to be
20050 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
20051
20052 /**
20053  * @class Roo.data.DataReader
20054  * Base class for reading structured data from a data source.  This class is intended to be
20055  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
20056  */
20057
20058 Roo.data.DataReader = function(meta, recordType){
20059     
20060     this.meta = meta;
20061     
20062     this.recordType = recordType instanceof Array ? 
20063         Roo.data.Record.create(recordType) : recordType;
20064 };
20065
20066 Roo.data.DataReader.prototype = {
20067      /**
20068      * Create an empty record
20069      * @param {Object} data (optional) - overlay some values
20070      * @return {Roo.data.Record} record created.
20071      */
20072     newRow :  function(d) {
20073         var da =  {};
20074         this.recordType.prototype.fields.each(function(c) {
20075             switch( c.type) {
20076                 case 'int' : da[c.name] = 0; break;
20077                 case 'date' : da[c.name] = new Date(); break;
20078                 case 'float' : da[c.name] = 0.0; break;
20079                 case 'boolean' : da[c.name] = false; break;
20080                 default : da[c.name] = ""; break;
20081             }
20082             
20083         });
20084         return new this.recordType(Roo.apply(da, d));
20085     }
20086     
20087 };/*
20088  * Based on:
20089  * Ext JS Library 1.1.1
20090  * Copyright(c) 2006-2007, Ext JS, LLC.
20091  *
20092  * Originally Released Under LGPL - original licence link has changed is not relivant.
20093  *
20094  * Fork - LGPL
20095  * <script type="text/javascript">
20096  */
20097
20098 /**
20099  * @class Roo.data.DataProxy
20100  * @extends Roo.data.Observable
20101  * This class is an abstract base class for implementations which provide retrieval of
20102  * unformatted data objects.<br>
20103  * <p>
20104  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
20105  * (of the appropriate type which knows how to parse the data object) to provide a block of
20106  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
20107  * <p>
20108  * Custom implementations must implement the load method as described in
20109  * {@link Roo.data.HttpProxy#load}.
20110  */
20111 Roo.data.DataProxy = function(){
20112     this.addEvents({
20113         /**
20114          * @event beforeload
20115          * Fires before a network request is made to retrieve a data object.
20116          * @param {Object} This DataProxy object.
20117          * @param {Object} params The params parameter to the load function.
20118          */
20119         beforeload : true,
20120         /**
20121          * @event load
20122          * Fires before the load method's callback is called.
20123          * @param {Object} This DataProxy object.
20124          * @param {Object} o The data object.
20125          * @param {Object} arg The callback argument object passed to the load function.
20126          */
20127         load : true,
20128         /**
20129          * @event loadexception
20130          * Fires if an Exception occurs during data retrieval.
20131          * @param {Object} This DataProxy object.
20132          * @param {Object} o The data object.
20133          * @param {Object} arg The callback argument object passed to the load function.
20134          * @param {Object} e The Exception.
20135          */
20136         loadexception : true
20137     });
20138     Roo.data.DataProxy.superclass.constructor.call(this);
20139 };
20140
20141 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
20142
20143     /**
20144      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
20145      */
20146 /*
20147  * Based on:
20148  * Ext JS Library 1.1.1
20149  * Copyright(c) 2006-2007, Ext JS, LLC.
20150  *
20151  * Originally Released Under LGPL - original licence link has changed is not relivant.
20152  *
20153  * Fork - LGPL
20154  * <script type="text/javascript">
20155  */
20156 /**
20157  * @class Roo.data.MemoryProxy
20158  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
20159  * to the Reader when its load method is called.
20160  * @constructor
20161  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
20162  */
20163 Roo.data.MemoryProxy = function(data){
20164     if (data.data) {
20165         data = data.data;
20166     }
20167     Roo.data.MemoryProxy.superclass.constructor.call(this);
20168     this.data = data;
20169 };
20170
20171 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
20172     /**
20173      * Load data from the requested source (in this case an in-memory
20174      * data object passed to the constructor), read the data object into
20175      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
20176      * process that block using the passed callback.
20177      * @param {Object} params This parameter is not used by the MemoryProxy class.
20178      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20179      * object into a block of Roo.data.Records.
20180      * @param {Function} callback The function into which to pass the block of Roo.data.records.
20181      * The function must be passed <ul>
20182      * <li>The Record block object</li>
20183      * <li>The "arg" argument from the load function</li>
20184      * <li>A boolean success indicator</li>
20185      * </ul>
20186      * @param {Object} scope The scope in which to call the callback
20187      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20188      */
20189     load : function(params, reader, callback, scope, arg){
20190         params = params || {};
20191         var result;
20192         try {
20193             result = reader.readRecords(this.data);
20194         }catch(e){
20195             this.fireEvent("loadexception", this, arg, null, e);
20196             callback.call(scope, null, arg, false);
20197             return;
20198         }
20199         callback.call(scope, result, arg, true);
20200     },
20201     
20202     // private
20203     update : function(params, records){
20204         
20205     }
20206 });/*
20207  * Based on:
20208  * Ext JS Library 1.1.1
20209  * Copyright(c) 2006-2007, Ext JS, LLC.
20210  *
20211  * Originally Released Under LGPL - original licence link has changed is not relivant.
20212  *
20213  * Fork - LGPL
20214  * <script type="text/javascript">
20215  */
20216 /**
20217  * @class Roo.data.HttpProxy
20218  * @extends Roo.data.DataProxy
20219  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
20220  * configured to reference a certain URL.<br><br>
20221  * <p>
20222  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
20223  * from which the running page was served.<br><br>
20224  * <p>
20225  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
20226  * <p>
20227  * Be aware that to enable the browser to parse an XML document, the server must set
20228  * the Content-Type header in the HTTP response to "text/xml".
20229  * @constructor
20230  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
20231  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
20232  * will be used to make the request.
20233  */
20234 Roo.data.HttpProxy = function(conn){
20235     Roo.data.HttpProxy.superclass.constructor.call(this);
20236     // is conn a conn config or a real conn?
20237     this.conn = conn;
20238     this.useAjax = !conn || !conn.events;
20239   
20240 };
20241
20242 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
20243     // thse are take from connection...
20244     
20245     /**
20246      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
20247      */
20248     /**
20249      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
20250      * extra parameters to each request made by this object. (defaults to undefined)
20251      */
20252     /**
20253      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
20254      *  to each request made by this object. (defaults to undefined)
20255      */
20256     /**
20257      * @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)
20258      */
20259     /**
20260      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
20261      */
20262      /**
20263      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
20264      * @type Boolean
20265      */
20266   
20267
20268     /**
20269      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
20270      * @type Boolean
20271      */
20272     /**
20273      * Return the {@link Roo.data.Connection} object being used by this Proxy.
20274      * @return {Connection} The Connection object. This object may be used to subscribe to events on
20275      * a finer-grained basis than the DataProxy events.
20276      */
20277     getConnection : function(){
20278         return this.useAjax ? Roo.Ajax : this.conn;
20279     },
20280
20281     /**
20282      * Load data from the configured {@link Roo.data.Connection}, read the data object into
20283      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
20284      * process that block using the passed callback.
20285      * @param {Object} params An object containing properties which are to be used as HTTP parameters
20286      * for the request to the remote server.
20287      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20288      * object into a block of Roo.data.Records.
20289      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
20290      * The function must be passed <ul>
20291      * <li>The Record block object</li>
20292      * <li>The "arg" argument from the load function</li>
20293      * <li>A boolean success indicator</li>
20294      * </ul>
20295      * @param {Object} scope The scope in which to call the callback
20296      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20297      */
20298     load : function(params, reader, callback, scope, arg){
20299         if(this.fireEvent("beforeload", this, params) !== false){
20300             var  o = {
20301                 params : params || {},
20302                 request: {
20303                     callback : callback,
20304                     scope : scope,
20305                     arg : arg
20306                 },
20307                 reader: reader,
20308                 callback : this.loadResponse,
20309                 scope: this
20310             };
20311             if(this.useAjax){
20312                 Roo.applyIf(o, this.conn);
20313                 if(this.activeRequest){
20314                     Roo.Ajax.abort(this.activeRequest);
20315                 }
20316                 this.activeRequest = Roo.Ajax.request(o);
20317             }else{
20318                 this.conn.request(o);
20319             }
20320         }else{
20321             callback.call(scope||this, null, arg, false);
20322         }
20323     },
20324
20325     // private
20326     loadResponse : function(o, success, response){
20327         delete this.activeRequest;
20328         if(!success){
20329             this.fireEvent("loadexception", this, o, response);
20330             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20331             return;
20332         }
20333         var result;
20334         try {
20335             result = o.reader.read(response);
20336         }catch(e){
20337             this.fireEvent("loadexception", this, o, response, e);
20338             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20339             return;
20340         }
20341         
20342         this.fireEvent("load", this, o, o.request.arg);
20343         o.request.callback.call(o.request.scope, result, o.request.arg, true);
20344     },
20345
20346     // private
20347     update : function(dataSet){
20348
20349     },
20350
20351     // private
20352     updateResponse : function(dataSet){
20353
20354     }
20355 });/*
20356  * Based on:
20357  * Ext JS Library 1.1.1
20358  * Copyright(c) 2006-2007, Ext JS, LLC.
20359  *
20360  * Originally Released Under LGPL - original licence link has changed is not relivant.
20361  *
20362  * Fork - LGPL
20363  * <script type="text/javascript">
20364  */
20365
20366 /**
20367  * @class Roo.data.ScriptTagProxy
20368  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
20369  * other than the originating domain of the running page.<br><br>
20370  * <p>
20371  * <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
20372  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
20373  * <p>
20374  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
20375  * source code that is used as the source inside a &lt;script> tag.<br><br>
20376  * <p>
20377  * In order for the browser to process the returned data, the server must wrap the data object
20378  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
20379  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
20380  * depending on whether the callback name was passed:
20381  * <p>
20382  * <pre><code>
20383 boolean scriptTag = false;
20384 String cb = request.getParameter("callback");
20385 if (cb != null) {
20386     scriptTag = true;
20387     response.setContentType("text/javascript");
20388 } else {
20389     response.setContentType("application/x-json");
20390 }
20391 Writer out = response.getWriter();
20392 if (scriptTag) {
20393     out.write(cb + "(");
20394 }
20395 out.print(dataBlock.toJsonString());
20396 if (scriptTag) {
20397     out.write(");");
20398 }
20399 </pre></code>
20400  *
20401  * @constructor
20402  * @param {Object} config A configuration object.
20403  */
20404 Roo.data.ScriptTagProxy = function(config){
20405     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
20406     Roo.apply(this, config);
20407     this.head = document.getElementsByTagName("head")[0];
20408 };
20409
20410 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
20411
20412 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
20413     /**
20414      * @cfg {String} url The URL from which to request the data object.
20415      */
20416     /**
20417      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
20418      */
20419     timeout : 30000,
20420     /**
20421      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
20422      * the server the name of the callback function set up by the load call to process the returned data object.
20423      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
20424      * javascript output which calls this named function passing the data object as its only parameter.
20425      */
20426     callbackParam : "callback",
20427     /**
20428      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
20429      * name to the request.
20430      */
20431     nocache : true,
20432
20433     /**
20434      * Load data from the configured URL, read the data object into
20435      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
20436      * process that block using the passed callback.
20437      * @param {Object} params An object containing properties which are to be used as HTTP parameters
20438      * for the request to the remote server.
20439      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20440      * object into a block of Roo.data.Records.
20441      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
20442      * The function must be passed <ul>
20443      * <li>The Record block object</li>
20444      * <li>The "arg" argument from the load function</li>
20445      * <li>A boolean success indicator</li>
20446      * </ul>
20447      * @param {Object} scope The scope in which to call the callback
20448      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20449      */
20450     load : function(params, reader, callback, scope, arg){
20451         if(this.fireEvent("beforeload", this, params) !== false){
20452
20453             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
20454
20455             var url = this.url;
20456             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
20457             if(this.nocache){
20458                 url += "&_dc=" + (new Date().getTime());
20459             }
20460             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
20461             var trans = {
20462                 id : transId,
20463                 cb : "stcCallback"+transId,
20464                 scriptId : "stcScript"+transId,
20465                 params : params,
20466                 arg : arg,
20467                 url : url,
20468                 callback : callback,
20469                 scope : scope,
20470                 reader : reader
20471             };
20472             var conn = this;
20473
20474             window[trans.cb] = function(o){
20475                 conn.handleResponse(o, trans);
20476             };
20477
20478             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
20479
20480             if(this.autoAbort !== false){
20481                 this.abort();
20482             }
20483
20484             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
20485
20486             var script = document.createElement("script");
20487             script.setAttribute("src", url);
20488             script.setAttribute("type", "text/javascript");
20489             script.setAttribute("id", trans.scriptId);
20490             this.head.appendChild(script);
20491
20492             this.trans = trans;
20493         }else{
20494             callback.call(scope||this, null, arg, false);
20495         }
20496     },
20497
20498     // private
20499     isLoading : function(){
20500         return this.trans ? true : false;
20501     },
20502
20503     /**
20504      * Abort the current server request.
20505      */
20506     abort : function(){
20507         if(this.isLoading()){
20508             this.destroyTrans(this.trans);
20509         }
20510     },
20511
20512     // private
20513     destroyTrans : function(trans, isLoaded){
20514         this.head.removeChild(document.getElementById(trans.scriptId));
20515         clearTimeout(trans.timeoutId);
20516         if(isLoaded){
20517             window[trans.cb] = undefined;
20518             try{
20519                 delete window[trans.cb];
20520             }catch(e){}
20521         }else{
20522             // if hasn't been loaded, wait for load to remove it to prevent script error
20523             window[trans.cb] = function(){
20524                 window[trans.cb] = undefined;
20525                 try{
20526                     delete window[trans.cb];
20527                 }catch(e){}
20528             };
20529         }
20530     },
20531
20532     // private
20533     handleResponse : function(o, trans){
20534         this.trans = false;
20535         this.destroyTrans(trans, true);
20536         var result;
20537         try {
20538             result = trans.reader.readRecords(o);
20539         }catch(e){
20540             this.fireEvent("loadexception", this, o, trans.arg, e);
20541             trans.callback.call(trans.scope||window, null, trans.arg, false);
20542             return;
20543         }
20544         this.fireEvent("load", this, o, trans.arg);
20545         trans.callback.call(trans.scope||window, result, trans.arg, true);
20546     },
20547
20548     // private
20549     handleFailure : function(trans){
20550         this.trans = false;
20551         this.destroyTrans(trans, false);
20552         this.fireEvent("loadexception", this, null, trans.arg);
20553         trans.callback.call(trans.scope||window, null, trans.arg, false);
20554     }
20555 });/*
20556  * Based on:
20557  * Ext JS Library 1.1.1
20558  * Copyright(c) 2006-2007, Ext JS, LLC.
20559  *
20560  * Originally Released Under LGPL - original licence link has changed is not relivant.
20561  *
20562  * Fork - LGPL
20563  * <script type="text/javascript">
20564  */
20565
20566 /**
20567  * @class Roo.data.JsonReader
20568  * @extends Roo.data.DataReader
20569  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
20570  * based on mappings in a provided Roo.data.Record constructor.
20571  * 
20572  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
20573  * in the reply previously. 
20574  * 
20575  * <p>
20576  * Example code:
20577  * <pre><code>
20578 var RecordDef = Roo.data.Record.create([
20579     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
20580     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
20581 ]);
20582 var myReader = new Roo.data.JsonReader({
20583     totalProperty: "results",    // The property which contains the total dataset size (optional)
20584     root: "rows",                // The property which contains an Array of row objects
20585     id: "id"                     // The property within each row object that provides an ID for the record (optional)
20586 }, RecordDef);
20587 </code></pre>
20588  * <p>
20589  * This would consume a JSON file like this:
20590  * <pre><code>
20591 { 'results': 2, 'rows': [
20592     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
20593     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
20594 }
20595 </code></pre>
20596  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
20597  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
20598  * paged from the remote server.
20599  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
20600  * @cfg {String} root name of the property which contains the Array of row objects.
20601  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
20602  * @constructor
20603  * Create a new JsonReader
20604  * @param {Object} meta Metadata configuration options
20605  * @param {Object} recordType Either an Array of field definition objects,
20606  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
20607  */
20608 Roo.data.JsonReader = function(meta, recordType){
20609     
20610     meta = meta || {};
20611     // set some defaults:
20612     Roo.applyIf(meta, {
20613         totalProperty: 'total',
20614         successProperty : 'success',
20615         root : 'data',
20616         id : 'id'
20617     });
20618     
20619     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
20620 };
20621 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
20622     
20623     /**
20624      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
20625      * Used by Store query builder to append _requestMeta to params.
20626      * 
20627      */
20628     metaFromRemote : false,
20629     /**
20630      * This method is only used by a DataProxy which has retrieved data from a remote server.
20631      * @param {Object} response The XHR object which contains the JSON data in its responseText.
20632      * @return {Object} data A data block which is used by an Roo.data.Store object as
20633      * a cache of Roo.data.Records.
20634      */
20635     read : function(response){
20636         var json = response.responseText;
20637        
20638         var o = /* eval:var:o */ eval("("+json+")");
20639         if(!o) {
20640             throw {message: "JsonReader.read: Json object not found"};
20641         }
20642         
20643         if(o.metaData){
20644             
20645             delete this.ef;
20646             this.metaFromRemote = true;
20647             this.meta = o.metaData;
20648             this.recordType = Roo.data.Record.create(o.metaData.fields);
20649             this.onMetaChange(this.meta, this.recordType, o);
20650         }
20651         return this.readRecords(o);
20652     },
20653
20654     // private function a store will implement
20655     onMetaChange : function(meta, recordType, o){
20656
20657     },
20658
20659     /**
20660          * @ignore
20661          */
20662     simpleAccess: function(obj, subsc) {
20663         return obj[subsc];
20664     },
20665
20666         /**
20667          * @ignore
20668          */
20669     getJsonAccessor: function(){
20670         var re = /[\[\.]/;
20671         return function(expr) {
20672             try {
20673                 return(re.test(expr))
20674                     ? new Function("obj", "return obj." + expr)
20675                     : function(obj){
20676                         return obj[expr];
20677                     };
20678             } catch(e){}
20679             return Roo.emptyFn;
20680         };
20681     }(),
20682
20683     /**
20684      * Create a data block containing Roo.data.Records from an XML document.
20685      * @param {Object} o An object which contains an Array of row objects in the property specified
20686      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
20687      * which contains the total size of the dataset.
20688      * @return {Object} data A data block which is used by an Roo.data.Store object as
20689      * a cache of Roo.data.Records.
20690      */
20691     readRecords : function(o){
20692         /**
20693          * After any data loads, the raw JSON data is available for further custom processing.
20694          * @type Object
20695          */
20696         this.jsonData = o;
20697         var s = this.meta, Record = this.recordType,
20698             f = Record.prototype.fields, fi = f.items, fl = f.length;
20699
20700 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
20701         if (!this.ef) {
20702             if(s.totalProperty) {
20703                     this.getTotal = this.getJsonAccessor(s.totalProperty);
20704                 }
20705                 if(s.successProperty) {
20706                     this.getSuccess = this.getJsonAccessor(s.successProperty);
20707                 }
20708                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
20709                 if (s.id) {
20710                         var g = this.getJsonAccessor(s.id);
20711                         this.getId = function(rec) {
20712                                 var r = g(rec);
20713                                 return (r === undefined || r === "") ? null : r;
20714                         };
20715                 } else {
20716                         this.getId = function(){return null;};
20717                 }
20718             this.ef = [];
20719             for(var jj = 0; jj < fl; jj++){
20720                 f = fi[jj];
20721                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
20722                 this.ef[jj] = this.getJsonAccessor(map);
20723             }
20724         }
20725
20726         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
20727         if(s.totalProperty){
20728             var vt = parseInt(this.getTotal(o), 10);
20729             if(!isNaN(vt)){
20730                 totalRecords = vt;
20731             }
20732         }
20733         if(s.successProperty){
20734             var vs = this.getSuccess(o);
20735             if(vs === false || vs === 'false'){
20736                 success = false;
20737             }
20738         }
20739         var records = [];
20740             for(var i = 0; i < c; i++){
20741                     var n = root[i];
20742                 var values = {};
20743                 var id = this.getId(n);
20744                 for(var j = 0; j < fl; j++){
20745                     f = fi[j];
20746                 var v = this.ef[j](n);
20747                 if (!f.convert) {
20748                     Roo.log('missing convert for ' + f.name);
20749                     Roo.log(f);
20750                     continue;
20751                 }
20752                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
20753                 }
20754                 var record = new Record(values, id);
20755                 record.json = n;
20756                 records[i] = record;
20757             }
20758             return {
20759                 success : success,
20760                 records : records,
20761                 totalRecords : totalRecords
20762             };
20763     }
20764 });/*
20765  * Based on:
20766  * Ext JS Library 1.1.1
20767  * Copyright(c) 2006-2007, Ext JS, LLC.
20768  *
20769  * Originally Released Under LGPL - original licence link has changed is not relivant.
20770  *
20771  * Fork - LGPL
20772  * <script type="text/javascript">
20773  */
20774
20775 /**
20776  * @class Roo.data.XmlReader
20777  * @extends Roo.data.DataReader
20778  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
20779  * based on mappings in a provided Roo.data.Record constructor.<br><br>
20780  * <p>
20781  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
20782  * header in the HTTP response must be set to "text/xml".</em>
20783  * <p>
20784  * Example code:
20785  * <pre><code>
20786 var RecordDef = Roo.data.Record.create([
20787    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
20788    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
20789 ]);
20790 var myReader = new Roo.data.XmlReader({
20791    totalRecords: "results", // The element which contains the total dataset size (optional)
20792    record: "row",           // The repeated element which contains row information
20793    id: "id"                 // The element within the row that provides an ID for the record (optional)
20794 }, RecordDef);
20795 </code></pre>
20796  * <p>
20797  * This would consume an XML file like this:
20798  * <pre><code>
20799 &lt;?xml?>
20800 &lt;dataset>
20801  &lt;results>2&lt;/results>
20802  &lt;row>
20803    &lt;id>1&lt;/id>
20804    &lt;name>Bill&lt;/name>
20805    &lt;occupation>Gardener&lt;/occupation>
20806  &lt;/row>
20807  &lt;row>
20808    &lt;id>2&lt;/id>
20809    &lt;name>Ben&lt;/name>
20810    &lt;occupation>Horticulturalist&lt;/occupation>
20811  &lt;/row>
20812 &lt;/dataset>
20813 </code></pre>
20814  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
20815  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
20816  * paged from the remote server.
20817  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
20818  * @cfg {String} success The DomQuery path to the success attribute used by forms.
20819  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
20820  * a record identifier value.
20821  * @constructor
20822  * Create a new XmlReader
20823  * @param {Object} meta Metadata configuration options
20824  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
20825  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
20826  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
20827  */
20828 Roo.data.XmlReader = function(meta, recordType){
20829     meta = meta || {};
20830     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
20831 };
20832 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
20833     /**
20834      * This method is only used by a DataProxy which has retrieved data from a remote server.
20835          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
20836          * to contain a method called 'responseXML' that returns an XML document object.
20837      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
20838      * a cache of Roo.data.Records.
20839      */
20840     read : function(response){
20841         var doc = response.responseXML;
20842         if(!doc) {
20843             throw {message: "XmlReader.read: XML Document not available"};
20844         }
20845         return this.readRecords(doc);
20846     },
20847
20848     /**
20849      * Create a data block containing Roo.data.Records from an XML document.
20850          * @param {Object} doc A parsed XML document.
20851      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
20852      * a cache of Roo.data.Records.
20853      */
20854     readRecords : function(doc){
20855         /**
20856          * After any data loads/reads, the raw XML Document is available for further custom processing.
20857          * @type XMLDocument
20858          */
20859         this.xmlData = doc;
20860         var root = doc.documentElement || doc;
20861         var q = Roo.DomQuery;
20862         var recordType = this.recordType, fields = recordType.prototype.fields;
20863         var sid = this.meta.id;
20864         var totalRecords = 0, success = true;
20865         if(this.meta.totalRecords){
20866             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
20867         }
20868         
20869         if(this.meta.success){
20870             var sv = q.selectValue(this.meta.success, root, true);
20871             success = sv !== false && sv !== 'false';
20872         }
20873         var records = [];
20874         var ns = q.select(this.meta.record, root);
20875         for(var i = 0, len = ns.length; i < len; i++) {
20876                 var n = ns[i];
20877                 var values = {};
20878                 var id = sid ? q.selectValue(sid, n) : undefined;
20879                 for(var j = 0, jlen = fields.length; j < jlen; j++){
20880                     var f = fields.items[j];
20881                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
20882                     v = f.convert(v);
20883                     values[f.name] = v;
20884                 }
20885                 var record = new recordType(values, id);
20886                 record.node = n;
20887                 records[records.length] = record;
20888             }
20889
20890             return {
20891                 success : success,
20892                 records : records,
20893                 totalRecords : totalRecords || records.length
20894             };
20895     }
20896 });/*
20897  * Based on:
20898  * Ext JS Library 1.1.1
20899  * Copyright(c) 2006-2007, Ext JS, LLC.
20900  *
20901  * Originally Released Under LGPL - original licence link has changed is not relivant.
20902  *
20903  * Fork - LGPL
20904  * <script type="text/javascript">
20905  */
20906
20907 /**
20908  * @class Roo.data.ArrayReader
20909  * @extends Roo.data.DataReader
20910  * Data reader class to create an Array of Roo.data.Record objects from an Array.
20911  * Each element of that Array represents a row of data fields. The
20912  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
20913  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
20914  * <p>
20915  * Example code:.
20916  * <pre><code>
20917 var RecordDef = Roo.data.Record.create([
20918     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
20919     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
20920 ]);
20921 var myReader = new Roo.data.ArrayReader({
20922     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
20923 }, RecordDef);
20924 </code></pre>
20925  * <p>
20926  * This would consume an Array like this:
20927  * <pre><code>
20928 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
20929   </code></pre>
20930  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
20931  * @constructor
20932  * Create a new JsonReader
20933  * @param {Object} meta Metadata configuration options.
20934  * @param {Object} recordType Either an Array of field definition objects
20935  * as specified to {@link Roo.data.Record#create},
20936  * or an {@link Roo.data.Record} object
20937  * created using {@link Roo.data.Record#create}.
20938  */
20939 Roo.data.ArrayReader = function(meta, recordType){
20940     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
20941 };
20942
20943 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
20944     /**
20945      * Create a data block containing Roo.data.Records from an XML document.
20946      * @param {Object} o An Array of row objects which represents the dataset.
20947      * @return {Object} data A data block which is used by an Roo.data.Store object as
20948      * a cache of Roo.data.Records.
20949      */
20950     readRecords : function(o){
20951         var sid = this.meta ? this.meta.id : null;
20952         var recordType = this.recordType, fields = recordType.prototype.fields;
20953         var records = [];
20954         var root = o;
20955             for(var i = 0; i < root.length; i++){
20956                     var n = root[i];
20957                 var values = {};
20958                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
20959                 for(var j = 0, jlen = fields.length; j < jlen; j++){
20960                 var f = fields.items[j];
20961                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
20962                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
20963                 v = f.convert(v);
20964                 values[f.name] = v;
20965             }
20966                 var record = new recordType(values, id);
20967                 record.json = n;
20968                 records[records.length] = record;
20969             }
20970             return {
20971                 records : records,
20972                 totalRecords : records.length
20973             };
20974     }
20975 });/*
20976  * Based on:
20977  * Ext JS Library 1.1.1
20978  * Copyright(c) 2006-2007, Ext JS, LLC.
20979  *
20980  * Originally Released Under LGPL - original licence link has changed is not relivant.
20981  *
20982  * Fork - LGPL
20983  * <script type="text/javascript">
20984  */
20985
20986
20987 /**
20988  * @class Roo.data.Tree
20989  * @extends Roo.util.Observable
20990  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
20991  * in the tree have most standard DOM functionality.
20992  * @constructor
20993  * @param {Node} root (optional) The root node
20994  */
20995 Roo.data.Tree = function(root){
20996    this.nodeHash = {};
20997    /**
20998     * The root node for this tree
20999     * @type Node
21000     */
21001    this.root = null;
21002    if(root){
21003        this.setRootNode(root);
21004    }
21005    this.addEvents({
21006        /**
21007         * @event append
21008         * Fires when a new child node is appended to a node in this tree.
21009         * @param {Tree} tree The owner tree
21010         * @param {Node} parent The parent node
21011         * @param {Node} node The newly appended node
21012         * @param {Number} index The index of the newly appended node
21013         */
21014        "append" : true,
21015        /**
21016         * @event remove
21017         * Fires when a child node is removed from a node in this tree.
21018         * @param {Tree} tree The owner tree
21019         * @param {Node} parent The parent node
21020         * @param {Node} node The child node removed
21021         */
21022        "remove" : true,
21023        /**
21024         * @event move
21025         * Fires when a node is moved to a new location in the tree
21026         * @param {Tree} tree The owner tree
21027         * @param {Node} node The node moved
21028         * @param {Node} oldParent The old parent of this node
21029         * @param {Node} newParent The new parent of this node
21030         * @param {Number} index The index it was moved to
21031         */
21032        "move" : true,
21033        /**
21034         * @event insert
21035         * Fires when a new child node is inserted in a node in this tree.
21036         * @param {Tree} tree The owner tree
21037         * @param {Node} parent The parent node
21038         * @param {Node} node The child node inserted
21039         * @param {Node} refNode The child node the node was inserted before
21040         */
21041        "insert" : true,
21042        /**
21043         * @event beforeappend
21044         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
21045         * @param {Tree} tree The owner tree
21046         * @param {Node} parent The parent node
21047         * @param {Node} node The child node to be appended
21048         */
21049        "beforeappend" : true,
21050        /**
21051         * @event beforeremove
21052         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
21053         * @param {Tree} tree The owner tree
21054         * @param {Node} parent The parent node
21055         * @param {Node} node The child node to be removed
21056         */
21057        "beforeremove" : true,
21058        /**
21059         * @event beforemove
21060         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
21061         * @param {Tree} tree The owner tree
21062         * @param {Node} node The node being moved
21063         * @param {Node} oldParent The parent of the node
21064         * @param {Node} newParent The new parent the node is moving to
21065         * @param {Number} index The index it is being moved to
21066         */
21067        "beforemove" : true,
21068        /**
21069         * @event beforeinsert
21070         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
21071         * @param {Tree} tree The owner tree
21072         * @param {Node} parent The parent node
21073         * @param {Node} node The child node to be inserted
21074         * @param {Node} refNode The child node the node is being inserted before
21075         */
21076        "beforeinsert" : true
21077    });
21078
21079     Roo.data.Tree.superclass.constructor.call(this);
21080 };
21081
21082 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
21083     pathSeparator: "/",
21084
21085     proxyNodeEvent : function(){
21086         return this.fireEvent.apply(this, arguments);
21087     },
21088
21089     /**
21090      * Returns the root node for this tree.
21091      * @return {Node}
21092      */
21093     getRootNode : function(){
21094         return this.root;
21095     },
21096
21097     /**
21098      * Sets the root node for this tree.
21099      * @param {Node} node
21100      * @return {Node}
21101      */
21102     setRootNode : function(node){
21103         this.root = node;
21104         node.ownerTree = this;
21105         node.isRoot = true;
21106         this.registerNode(node);
21107         return node;
21108     },
21109
21110     /**
21111      * Gets a node in this tree by its id.
21112      * @param {String} id
21113      * @return {Node}
21114      */
21115     getNodeById : function(id){
21116         return this.nodeHash[id];
21117     },
21118
21119     registerNode : function(node){
21120         this.nodeHash[node.id] = node;
21121     },
21122
21123     unregisterNode : function(node){
21124         delete this.nodeHash[node.id];
21125     },
21126
21127     toString : function(){
21128         return "[Tree"+(this.id?" "+this.id:"")+"]";
21129     }
21130 });
21131
21132 /**
21133  * @class Roo.data.Node
21134  * @extends Roo.util.Observable
21135  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
21136  * @cfg {String} id The id for this node. If one is not specified, one is generated.
21137  * @constructor
21138  * @param {Object} attributes The attributes/config for the node
21139  */
21140 Roo.data.Node = function(attributes){
21141     /**
21142      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
21143      * @type {Object}
21144      */
21145     this.attributes = attributes || {};
21146     this.leaf = this.attributes.leaf;
21147     /**
21148      * The node id. @type String
21149      */
21150     this.id = this.attributes.id;
21151     if(!this.id){
21152         this.id = Roo.id(null, "ynode-");
21153         this.attributes.id = this.id;
21154     }
21155     /**
21156      * All child nodes of this node. @type Array
21157      */
21158     this.childNodes = [];
21159     if(!this.childNodes.indexOf){ // indexOf is a must
21160         this.childNodes.indexOf = function(o){
21161             for(var i = 0, len = this.length; i < len; i++){
21162                 if(this[i] == o) {
21163                     return i;
21164                 }
21165             }
21166             return -1;
21167         };
21168     }
21169     /**
21170      * The parent node for this node. @type Node
21171      */
21172     this.parentNode = null;
21173     /**
21174      * The first direct child node of this node, or null if this node has no child nodes. @type Node
21175      */
21176     this.firstChild = null;
21177     /**
21178      * The last direct child node of this node, or null if this node has no child nodes. @type Node
21179      */
21180     this.lastChild = null;
21181     /**
21182      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
21183      */
21184     this.previousSibling = null;
21185     /**
21186      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
21187      */
21188     this.nextSibling = null;
21189
21190     this.addEvents({
21191        /**
21192         * @event append
21193         * Fires when a new child node is appended
21194         * @param {Tree} tree The owner tree
21195         * @param {Node} this This node
21196         * @param {Node} node The newly appended node
21197         * @param {Number} index The index of the newly appended node
21198         */
21199        "append" : true,
21200        /**
21201         * @event remove
21202         * Fires when a child node is removed
21203         * @param {Tree} tree The owner tree
21204         * @param {Node} this This node
21205         * @param {Node} node The removed node
21206         */
21207        "remove" : true,
21208        /**
21209         * @event move
21210         * Fires when this node is moved to a new location in the tree
21211         * @param {Tree} tree The owner tree
21212         * @param {Node} this This node
21213         * @param {Node} oldParent The old parent of this node
21214         * @param {Node} newParent The new parent of this node
21215         * @param {Number} index The index it was moved to
21216         */
21217        "move" : true,
21218        /**
21219         * @event insert
21220         * Fires when a new child node is inserted.
21221         * @param {Tree} tree The owner tree
21222         * @param {Node} this This node
21223         * @param {Node} node The child node inserted
21224         * @param {Node} refNode The child node the node was inserted before
21225         */
21226        "insert" : true,
21227        /**
21228         * @event beforeappend
21229         * Fires before a new child is appended, return false to cancel the append.
21230         * @param {Tree} tree The owner tree
21231         * @param {Node} this This node
21232         * @param {Node} node The child node to be appended
21233         */
21234        "beforeappend" : true,
21235        /**
21236         * @event beforeremove
21237         * Fires before a child is removed, return false to cancel the remove.
21238         * @param {Tree} tree The owner tree
21239         * @param {Node} this This node
21240         * @param {Node} node The child node to be removed
21241         */
21242        "beforeremove" : true,
21243        /**
21244         * @event beforemove
21245         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
21246         * @param {Tree} tree The owner tree
21247         * @param {Node} this This node
21248         * @param {Node} oldParent The parent of this node
21249         * @param {Node} newParent The new parent this node is moving to
21250         * @param {Number} index The index it is being moved to
21251         */
21252        "beforemove" : true,
21253        /**
21254         * @event beforeinsert
21255         * Fires before a new child is inserted, return false to cancel the insert.
21256         * @param {Tree} tree The owner tree
21257         * @param {Node} this This node
21258         * @param {Node} node The child node to be inserted
21259         * @param {Node} refNode The child node the node is being inserted before
21260         */
21261        "beforeinsert" : true
21262    });
21263     this.listeners = this.attributes.listeners;
21264     Roo.data.Node.superclass.constructor.call(this);
21265 };
21266
21267 Roo.extend(Roo.data.Node, Roo.util.Observable, {
21268     fireEvent : function(evtName){
21269         // first do standard event for this node
21270         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
21271             return false;
21272         }
21273         // then bubble it up to the tree if the event wasn't cancelled
21274         var ot = this.getOwnerTree();
21275         if(ot){
21276             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
21277                 return false;
21278             }
21279         }
21280         return true;
21281     },
21282
21283     /**
21284      * Returns true if this node is a leaf
21285      * @return {Boolean}
21286      */
21287     isLeaf : function(){
21288         return this.leaf === true;
21289     },
21290
21291     // private
21292     setFirstChild : function(node){
21293         this.firstChild = node;
21294     },
21295
21296     //private
21297     setLastChild : function(node){
21298         this.lastChild = node;
21299     },
21300
21301
21302     /**
21303      * Returns true if this node is the last child of its parent
21304      * @return {Boolean}
21305      */
21306     isLast : function(){
21307        return (!this.parentNode ? true : this.parentNode.lastChild == this);
21308     },
21309
21310     /**
21311      * Returns true if this node is the first child of its parent
21312      * @return {Boolean}
21313      */
21314     isFirst : function(){
21315        return (!this.parentNode ? true : this.parentNode.firstChild == this);
21316     },
21317
21318     hasChildNodes : function(){
21319         return !this.isLeaf() && this.childNodes.length > 0;
21320     },
21321
21322     /**
21323      * Insert node(s) as the last child node of this node.
21324      * @param {Node/Array} node The node or Array of nodes to append
21325      * @return {Node} The appended node if single append, or null if an array was passed
21326      */
21327     appendChild : function(node){
21328         var multi = false;
21329         if(node instanceof Array){
21330             multi = node;
21331         }else if(arguments.length > 1){
21332             multi = arguments;
21333         }
21334         // if passed an array or multiple args do them one by one
21335         if(multi){
21336             for(var i = 0, len = multi.length; i < len; i++) {
21337                 this.appendChild(multi[i]);
21338             }
21339         }else{
21340             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
21341                 return false;
21342             }
21343             var index = this.childNodes.length;
21344             var oldParent = node.parentNode;
21345             // it's a move, make sure we move it cleanly
21346             if(oldParent){
21347                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
21348                     return false;
21349                 }
21350                 oldParent.removeChild(node);
21351             }
21352             index = this.childNodes.length;
21353             if(index == 0){
21354                 this.setFirstChild(node);
21355             }
21356             this.childNodes.push(node);
21357             node.parentNode = this;
21358             var ps = this.childNodes[index-1];
21359             if(ps){
21360                 node.previousSibling = ps;
21361                 ps.nextSibling = node;
21362             }else{
21363                 node.previousSibling = null;
21364             }
21365             node.nextSibling = null;
21366             this.setLastChild(node);
21367             node.setOwnerTree(this.getOwnerTree());
21368             this.fireEvent("append", this.ownerTree, this, node, index);
21369             if(oldParent){
21370                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
21371             }
21372             return node;
21373         }
21374     },
21375
21376     /**
21377      * Removes a child node from this node.
21378      * @param {Node} node The node to remove
21379      * @return {Node} The removed node
21380      */
21381     removeChild : function(node){
21382         var index = this.childNodes.indexOf(node);
21383         if(index == -1){
21384             return false;
21385         }
21386         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
21387             return false;
21388         }
21389
21390         // remove it from childNodes collection
21391         this.childNodes.splice(index, 1);
21392
21393         // update siblings
21394         if(node.previousSibling){
21395             node.previousSibling.nextSibling = node.nextSibling;
21396         }
21397         if(node.nextSibling){
21398             node.nextSibling.previousSibling = node.previousSibling;
21399         }
21400
21401         // update child refs
21402         if(this.firstChild == node){
21403             this.setFirstChild(node.nextSibling);
21404         }
21405         if(this.lastChild == node){
21406             this.setLastChild(node.previousSibling);
21407         }
21408
21409         node.setOwnerTree(null);
21410         // clear any references from the node
21411         node.parentNode = null;
21412         node.previousSibling = null;
21413         node.nextSibling = null;
21414         this.fireEvent("remove", this.ownerTree, this, node);
21415         return node;
21416     },
21417
21418     /**
21419      * Inserts the first node before the second node in this nodes childNodes collection.
21420      * @param {Node} node The node to insert
21421      * @param {Node} refNode The node to insert before (if null the node is appended)
21422      * @return {Node} The inserted node
21423      */
21424     insertBefore : function(node, refNode){
21425         if(!refNode){ // like standard Dom, refNode can be null for append
21426             return this.appendChild(node);
21427         }
21428         // nothing to do
21429         if(node == refNode){
21430             return false;
21431         }
21432
21433         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
21434             return false;
21435         }
21436         var index = this.childNodes.indexOf(refNode);
21437         var oldParent = node.parentNode;
21438         var refIndex = index;
21439
21440         // when moving internally, indexes will change after remove
21441         if(oldParent == this && this.childNodes.indexOf(node) < index){
21442             refIndex--;
21443         }
21444
21445         // it's a move, make sure we move it cleanly
21446         if(oldParent){
21447             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
21448                 return false;
21449             }
21450             oldParent.removeChild(node);
21451         }
21452         if(refIndex == 0){
21453             this.setFirstChild(node);
21454         }
21455         this.childNodes.splice(refIndex, 0, node);
21456         node.parentNode = this;
21457         var ps = this.childNodes[refIndex-1];
21458         if(ps){
21459             node.previousSibling = ps;
21460             ps.nextSibling = node;
21461         }else{
21462             node.previousSibling = null;
21463         }
21464         node.nextSibling = refNode;
21465         refNode.previousSibling = node;
21466         node.setOwnerTree(this.getOwnerTree());
21467         this.fireEvent("insert", this.ownerTree, this, node, refNode);
21468         if(oldParent){
21469             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
21470         }
21471         return node;
21472     },
21473
21474     /**
21475      * Returns the child node at the specified index.
21476      * @param {Number} index
21477      * @return {Node}
21478      */
21479     item : function(index){
21480         return this.childNodes[index];
21481     },
21482
21483     /**
21484      * Replaces one child node in this node with another.
21485      * @param {Node} newChild The replacement node
21486      * @param {Node} oldChild The node to replace
21487      * @return {Node} The replaced node
21488      */
21489     replaceChild : function(newChild, oldChild){
21490         this.insertBefore(newChild, oldChild);
21491         this.removeChild(oldChild);
21492         return oldChild;
21493     },
21494
21495     /**
21496      * Returns the index of a child node
21497      * @param {Node} node
21498      * @return {Number} The index of the node or -1 if it was not found
21499      */
21500     indexOf : function(child){
21501         return this.childNodes.indexOf(child);
21502     },
21503
21504     /**
21505      * Returns the tree this node is in.
21506      * @return {Tree}
21507      */
21508     getOwnerTree : function(){
21509         // if it doesn't have one, look for one
21510         if(!this.ownerTree){
21511             var p = this;
21512             while(p){
21513                 if(p.ownerTree){
21514                     this.ownerTree = p.ownerTree;
21515                     break;
21516                 }
21517                 p = p.parentNode;
21518             }
21519         }
21520         return this.ownerTree;
21521     },
21522
21523     /**
21524      * Returns depth of this node (the root node has a depth of 0)
21525      * @return {Number}
21526      */
21527     getDepth : function(){
21528         var depth = 0;
21529         var p = this;
21530         while(p.parentNode){
21531             ++depth;
21532             p = p.parentNode;
21533         }
21534         return depth;
21535     },
21536
21537     // private
21538     setOwnerTree : function(tree){
21539         // if it's move, we need to update everyone
21540         if(tree != this.ownerTree){
21541             if(this.ownerTree){
21542                 this.ownerTree.unregisterNode(this);
21543             }
21544             this.ownerTree = tree;
21545             var cs = this.childNodes;
21546             for(var i = 0, len = cs.length; i < len; i++) {
21547                 cs[i].setOwnerTree(tree);
21548             }
21549             if(tree){
21550                 tree.registerNode(this);
21551             }
21552         }
21553     },
21554
21555     /**
21556      * Returns the path for this node. The path can be used to expand or select this node programmatically.
21557      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
21558      * @return {String} The path
21559      */
21560     getPath : function(attr){
21561         attr = attr || "id";
21562         var p = this.parentNode;
21563         var b = [this.attributes[attr]];
21564         while(p){
21565             b.unshift(p.attributes[attr]);
21566             p = p.parentNode;
21567         }
21568         var sep = this.getOwnerTree().pathSeparator;
21569         return sep + b.join(sep);
21570     },
21571
21572     /**
21573      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
21574      * function call will be the scope provided or the current node. The arguments to the function
21575      * will be the args provided or the current node. If the function returns false at any point,
21576      * the bubble is stopped.
21577      * @param {Function} fn The function to call
21578      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21579      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21580      */
21581     bubble : function(fn, scope, args){
21582         var p = this;
21583         while(p){
21584             if(fn.call(scope || p, args || p) === false){
21585                 break;
21586             }
21587             p = p.parentNode;
21588         }
21589     },
21590
21591     /**
21592      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
21593      * function call will be the scope provided or the current node. The arguments to the function
21594      * will be the args provided or the current node. If the function returns false at any point,
21595      * the cascade is stopped on that branch.
21596      * @param {Function} fn The function to call
21597      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21598      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21599      */
21600     cascade : function(fn, scope, args){
21601         if(fn.call(scope || this, args || this) !== false){
21602             var cs = this.childNodes;
21603             for(var i = 0, len = cs.length; i < len; i++) {
21604                 cs[i].cascade(fn, scope, args);
21605             }
21606         }
21607     },
21608
21609     /**
21610      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
21611      * function call will be the scope provided or the current node. The arguments to the function
21612      * will be the args provided or the current node. If the function returns false at any point,
21613      * the iteration stops.
21614      * @param {Function} fn The function to call
21615      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21616      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21617      */
21618     eachChild : function(fn, scope, args){
21619         var cs = this.childNodes;
21620         for(var i = 0, len = cs.length; i < len; i++) {
21621                 if(fn.call(scope || this, args || cs[i]) === false){
21622                     break;
21623                 }
21624         }
21625     },
21626
21627     /**
21628      * Finds the first child that has the attribute with the specified value.
21629      * @param {String} attribute The attribute name
21630      * @param {Mixed} value The value to search for
21631      * @return {Node} The found child or null if none was found
21632      */
21633     findChild : function(attribute, value){
21634         var cs = this.childNodes;
21635         for(var i = 0, len = cs.length; i < len; i++) {
21636                 if(cs[i].attributes[attribute] == value){
21637                     return cs[i];
21638                 }
21639         }
21640         return null;
21641     },
21642
21643     /**
21644      * Finds the first child by a custom function. The child matches if the function passed
21645      * returns true.
21646      * @param {Function} fn
21647      * @param {Object} scope (optional)
21648      * @return {Node} The found child or null if none was found
21649      */
21650     findChildBy : function(fn, scope){
21651         var cs = this.childNodes;
21652         for(var i = 0, len = cs.length; i < len; i++) {
21653                 if(fn.call(scope||cs[i], cs[i]) === true){
21654                     return cs[i];
21655                 }
21656         }
21657         return null;
21658     },
21659
21660     /**
21661      * Sorts this nodes children using the supplied sort function
21662      * @param {Function} fn
21663      * @param {Object} scope (optional)
21664      */
21665     sort : function(fn, scope){
21666         var cs = this.childNodes;
21667         var len = cs.length;
21668         if(len > 0){
21669             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
21670             cs.sort(sortFn);
21671             for(var i = 0; i < len; i++){
21672                 var n = cs[i];
21673                 n.previousSibling = cs[i-1];
21674                 n.nextSibling = cs[i+1];
21675                 if(i == 0){
21676                     this.setFirstChild(n);
21677                 }
21678                 if(i == len-1){
21679                     this.setLastChild(n);
21680                 }
21681             }
21682         }
21683     },
21684
21685     /**
21686      * Returns true if this node is an ancestor (at any point) of the passed node.
21687      * @param {Node} node
21688      * @return {Boolean}
21689      */
21690     contains : function(node){
21691         return node.isAncestor(this);
21692     },
21693
21694     /**
21695      * Returns true if the passed node is an ancestor (at any point) of this node.
21696      * @param {Node} node
21697      * @return {Boolean}
21698      */
21699     isAncestor : function(node){
21700         var p = this.parentNode;
21701         while(p){
21702             if(p == node){
21703                 return true;
21704             }
21705             p = p.parentNode;
21706         }
21707         return false;
21708     },
21709
21710     toString : function(){
21711         return "[Node"+(this.id?" "+this.id:"")+"]";
21712     }
21713 });/*
21714  * Based on:
21715  * Ext JS Library 1.1.1
21716  * Copyright(c) 2006-2007, Ext JS, LLC.
21717  *
21718  * Originally Released Under LGPL - original licence link has changed is not relivant.
21719  *
21720  * Fork - LGPL
21721  * <script type="text/javascript">
21722  */
21723  
21724
21725 /**
21726  * @class Roo.ComponentMgr
21727  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
21728  * @singleton
21729  */
21730 Roo.ComponentMgr = function(){
21731     var all = new Roo.util.MixedCollection();
21732
21733     return {
21734         /**
21735          * Registers a component.
21736          * @param {Roo.Component} c The component
21737          */
21738         register : function(c){
21739             all.add(c);
21740         },
21741
21742         /**
21743          * Unregisters a component.
21744          * @param {Roo.Component} c The component
21745          */
21746         unregister : function(c){
21747             all.remove(c);
21748         },
21749
21750         /**
21751          * Returns a component by id
21752          * @param {String} id The component id
21753          */
21754         get : function(id){
21755             return all.get(id);
21756         },
21757
21758         /**
21759          * Registers a function that will be called when a specified component is added to ComponentMgr
21760          * @param {String} id The component id
21761          * @param {Funtction} fn The callback function
21762          * @param {Object} scope The scope of the callback
21763          */
21764         onAvailable : function(id, fn, scope){
21765             all.on("add", function(index, o){
21766                 if(o.id == id){
21767                     fn.call(scope || o, o);
21768                     all.un("add", fn, scope);
21769                 }
21770             });
21771         }
21772     };
21773 }();/*
21774  * Based on:
21775  * Ext JS Library 1.1.1
21776  * Copyright(c) 2006-2007, Ext JS, LLC.
21777  *
21778  * Originally Released Under LGPL - original licence link has changed is not relivant.
21779  *
21780  * Fork - LGPL
21781  * <script type="text/javascript">
21782  */
21783  
21784 /**
21785  * @class Roo.Component
21786  * @extends Roo.util.Observable
21787  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
21788  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
21789  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
21790  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
21791  * All visual components (widgets) that require rendering into a layout should subclass Component.
21792  * @constructor
21793  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
21794  * 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
21795  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
21796  */
21797 Roo.Component = function(config){
21798     config = config || {};
21799     if(config.tagName || config.dom || typeof config == "string"){ // element object
21800         config = {el: config, id: config.id || config};
21801     }
21802     this.initialConfig = config;
21803
21804     Roo.apply(this, config);
21805     this.addEvents({
21806         /**
21807          * @event disable
21808          * Fires after the component is disabled.
21809              * @param {Roo.Component} this
21810              */
21811         disable : true,
21812         /**
21813          * @event enable
21814          * Fires after the component is enabled.
21815              * @param {Roo.Component} this
21816              */
21817         enable : true,
21818         /**
21819          * @event beforeshow
21820          * Fires before the component is shown.  Return false to stop the show.
21821              * @param {Roo.Component} this
21822              */
21823         beforeshow : true,
21824         /**
21825          * @event show
21826          * Fires after the component is shown.
21827              * @param {Roo.Component} this
21828              */
21829         show : true,
21830         /**
21831          * @event beforehide
21832          * Fires before the component is hidden. Return false to stop the hide.
21833              * @param {Roo.Component} this
21834              */
21835         beforehide : true,
21836         /**
21837          * @event hide
21838          * Fires after the component is hidden.
21839              * @param {Roo.Component} this
21840              */
21841         hide : true,
21842         /**
21843          * @event beforerender
21844          * Fires before the component is rendered. Return false to stop the render.
21845              * @param {Roo.Component} this
21846              */
21847         beforerender : true,
21848         /**
21849          * @event render
21850          * Fires after the component is rendered.
21851              * @param {Roo.Component} this
21852              */
21853         render : true,
21854         /**
21855          * @event beforedestroy
21856          * Fires before the component is destroyed. Return false to stop the destroy.
21857              * @param {Roo.Component} this
21858              */
21859         beforedestroy : true,
21860         /**
21861          * @event destroy
21862          * Fires after the component is destroyed.
21863              * @param {Roo.Component} this
21864              */
21865         destroy : true
21866     });
21867     if(!this.id){
21868         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
21869     }
21870     Roo.ComponentMgr.register(this);
21871     Roo.Component.superclass.constructor.call(this);
21872     this.initComponent();
21873     if(this.renderTo){ // not supported by all components yet. use at your own risk!
21874         this.render(this.renderTo);
21875         delete this.renderTo;
21876     }
21877 };
21878
21879 /** @private */
21880 Roo.Component.AUTO_ID = 1000;
21881
21882 Roo.extend(Roo.Component, Roo.util.Observable, {
21883     /**
21884      * @scope Roo.Component.prototype
21885      * @type {Boolean}
21886      * true if this component is hidden. Read-only.
21887      */
21888     hidden : false,
21889     /**
21890      * @type {Boolean}
21891      * true if this component is disabled. Read-only.
21892      */
21893     disabled : false,
21894     /**
21895      * @type {Boolean}
21896      * true if this component has been rendered. Read-only.
21897      */
21898     rendered : false,
21899     
21900     /** @cfg {String} disableClass
21901      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
21902      */
21903     disabledClass : "x-item-disabled",
21904         /** @cfg {Boolean} allowDomMove
21905          * Whether the component can move the Dom node when rendering (defaults to true).
21906          */
21907     allowDomMove : true,
21908     /** @cfg {String} hideMode
21909      * How this component should hidden. Supported values are
21910      * "visibility" (css visibility), "offsets" (negative offset position) and
21911      * "display" (css display) - defaults to "display".
21912      */
21913     hideMode: 'display',
21914
21915     /** @private */
21916     ctype : "Roo.Component",
21917
21918     /**
21919      * @cfg {String} actionMode 
21920      * which property holds the element that used for  hide() / show() / disable() / enable()
21921      * default is 'el' 
21922      */
21923     actionMode : "el",
21924
21925     /** @private */
21926     getActionEl : function(){
21927         return this[this.actionMode];
21928     },
21929
21930     initComponent : Roo.emptyFn,
21931     /**
21932      * If this is a lazy rendering component, render it to its container element.
21933      * @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.
21934      */
21935     render : function(container, position){
21936         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
21937             if(!container && this.el){
21938                 this.el = Roo.get(this.el);
21939                 container = this.el.dom.parentNode;
21940                 this.allowDomMove = false;
21941             }
21942             this.container = Roo.get(container);
21943             this.rendered = true;
21944             if(position !== undefined){
21945                 if(typeof position == 'number'){
21946                     position = this.container.dom.childNodes[position];
21947                 }else{
21948                     position = Roo.getDom(position);
21949                 }
21950             }
21951             this.onRender(this.container, position || null);
21952             if(this.cls){
21953                 this.el.addClass(this.cls);
21954                 delete this.cls;
21955             }
21956             if(this.style){
21957                 this.el.applyStyles(this.style);
21958                 delete this.style;
21959             }
21960             this.fireEvent("render", this);
21961             this.afterRender(this.container);
21962             if(this.hidden){
21963                 this.hide();
21964             }
21965             if(this.disabled){
21966                 this.disable();
21967             }
21968         }
21969         return this;
21970     },
21971
21972     /** @private */
21973     // default function is not really useful
21974     onRender : function(ct, position){
21975         if(this.el){
21976             this.el = Roo.get(this.el);
21977             if(this.allowDomMove !== false){
21978                 ct.dom.insertBefore(this.el.dom, position);
21979             }
21980         }
21981     },
21982
21983     /** @private */
21984     getAutoCreate : function(){
21985         var cfg = typeof this.autoCreate == "object" ?
21986                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
21987         if(this.id && !cfg.id){
21988             cfg.id = this.id;
21989         }
21990         return cfg;
21991     },
21992
21993     /** @private */
21994     afterRender : Roo.emptyFn,
21995
21996     /**
21997      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
21998      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
21999      */
22000     destroy : function(){
22001         if(this.fireEvent("beforedestroy", this) !== false){
22002             this.purgeListeners();
22003             this.beforeDestroy();
22004             if(this.rendered){
22005                 this.el.removeAllListeners();
22006                 this.el.remove();
22007                 if(this.actionMode == "container"){
22008                     this.container.remove();
22009                 }
22010             }
22011             this.onDestroy();
22012             Roo.ComponentMgr.unregister(this);
22013             this.fireEvent("destroy", this);
22014         }
22015     },
22016
22017         /** @private */
22018     beforeDestroy : function(){
22019
22020     },
22021
22022         /** @private */
22023         onDestroy : function(){
22024
22025     },
22026
22027     /**
22028      * Returns the underlying {@link Roo.Element}.
22029      * @return {Roo.Element} The element
22030      */
22031     getEl : function(){
22032         return this.el;
22033     },
22034
22035     /**
22036      * Returns the id of this component.
22037      * @return {String}
22038      */
22039     getId : function(){
22040         return this.id;
22041     },
22042
22043     /**
22044      * Try to focus this component.
22045      * @param {Boolean} selectText True to also select the text in this component (if applicable)
22046      * @return {Roo.Component} this
22047      */
22048     focus : function(selectText){
22049         if(this.rendered){
22050             this.el.focus();
22051             if(selectText === true){
22052                 this.el.dom.select();
22053             }
22054         }
22055         return this;
22056     },
22057
22058     /** @private */
22059     blur : function(){
22060         if(this.rendered){
22061             this.el.blur();
22062         }
22063         return this;
22064     },
22065
22066     /**
22067      * Disable this component.
22068      * @return {Roo.Component} this
22069      */
22070     disable : function(){
22071         if(this.rendered){
22072             this.onDisable();
22073         }
22074         this.disabled = true;
22075         this.fireEvent("disable", this);
22076         return this;
22077     },
22078
22079         // private
22080     onDisable : function(){
22081         this.getActionEl().addClass(this.disabledClass);
22082         this.el.dom.disabled = true;
22083     },
22084
22085     /**
22086      * Enable this component.
22087      * @return {Roo.Component} this
22088      */
22089     enable : function(){
22090         if(this.rendered){
22091             this.onEnable();
22092         }
22093         this.disabled = false;
22094         this.fireEvent("enable", this);
22095         return this;
22096     },
22097
22098         // private
22099     onEnable : function(){
22100         this.getActionEl().removeClass(this.disabledClass);
22101         this.el.dom.disabled = false;
22102     },
22103
22104     /**
22105      * Convenience function for setting disabled/enabled by boolean.
22106      * @param {Boolean} disabled
22107      */
22108     setDisabled : function(disabled){
22109         this[disabled ? "disable" : "enable"]();
22110     },
22111
22112     /**
22113      * Show this component.
22114      * @return {Roo.Component} this
22115      */
22116     show: function(){
22117         if(this.fireEvent("beforeshow", this) !== false){
22118             this.hidden = false;
22119             if(this.rendered){
22120                 this.onShow();
22121             }
22122             this.fireEvent("show", this);
22123         }
22124         return this;
22125     },
22126
22127     // private
22128     onShow : function(){
22129         var ae = this.getActionEl();
22130         if(this.hideMode == 'visibility'){
22131             ae.dom.style.visibility = "visible";
22132         }else if(this.hideMode == 'offsets'){
22133             ae.removeClass('x-hidden');
22134         }else{
22135             ae.dom.style.display = "";
22136         }
22137     },
22138
22139     /**
22140      * Hide this component.
22141      * @return {Roo.Component} this
22142      */
22143     hide: function(){
22144         if(this.fireEvent("beforehide", this) !== false){
22145             this.hidden = true;
22146             if(this.rendered){
22147                 this.onHide();
22148             }
22149             this.fireEvent("hide", this);
22150         }
22151         return this;
22152     },
22153
22154     // private
22155     onHide : function(){
22156         var ae = this.getActionEl();
22157         if(this.hideMode == 'visibility'){
22158             ae.dom.style.visibility = "hidden";
22159         }else if(this.hideMode == 'offsets'){
22160             ae.addClass('x-hidden');
22161         }else{
22162             ae.dom.style.display = "none";
22163         }
22164     },
22165
22166     /**
22167      * Convenience function to hide or show this component by boolean.
22168      * @param {Boolean} visible True to show, false to hide
22169      * @return {Roo.Component} this
22170      */
22171     setVisible: function(visible){
22172         if(visible) {
22173             this.show();
22174         }else{
22175             this.hide();
22176         }
22177         return this;
22178     },
22179
22180     /**
22181      * Returns true if this component is visible.
22182      */
22183     isVisible : function(){
22184         return this.getActionEl().isVisible();
22185     },
22186
22187     cloneConfig : function(overrides){
22188         overrides = overrides || {};
22189         var id = overrides.id || Roo.id();
22190         var cfg = Roo.applyIf(overrides, this.initialConfig);
22191         cfg.id = id; // prevent dup id
22192         return new this.constructor(cfg);
22193     }
22194 });/*
22195  * Based on:
22196  * Ext JS Library 1.1.1
22197  * Copyright(c) 2006-2007, Ext JS, LLC.
22198  *
22199  * Originally Released Under LGPL - original licence link has changed is not relivant.
22200  *
22201  * Fork - LGPL
22202  * <script type="text/javascript">
22203  */
22204  (function(){ 
22205 /**
22206  * @class Roo.Layer
22207  * @extends Roo.Element
22208  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
22209  * automatic maintaining of shadow/shim positions.
22210  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
22211  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
22212  * you can pass a string with a CSS class name. False turns off the shadow.
22213  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
22214  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
22215  * @cfg {String} cls CSS class to add to the element
22216  * @cfg {Number} zindex Starting z-index (defaults to 11000)
22217  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
22218  * @constructor
22219  * @param {Object} config An object with config options.
22220  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
22221  */
22222
22223 Roo.Layer = function(config, existingEl){
22224     config = config || {};
22225     var dh = Roo.DomHelper;
22226     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
22227     if(existingEl){
22228         this.dom = Roo.getDom(existingEl);
22229     }
22230     if(!this.dom){
22231         var o = config.dh || {tag: "div", cls: "x-layer"};
22232         this.dom = dh.append(pel, o);
22233     }
22234     if(config.cls){
22235         this.addClass(config.cls);
22236     }
22237     this.constrain = config.constrain !== false;
22238     this.visibilityMode = Roo.Element.VISIBILITY;
22239     if(config.id){
22240         this.id = this.dom.id = config.id;
22241     }else{
22242         this.id = Roo.id(this.dom);
22243     }
22244     this.zindex = config.zindex || this.getZIndex();
22245     this.position("absolute", this.zindex);
22246     if(config.shadow){
22247         this.shadowOffset = config.shadowOffset || 4;
22248         this.shadow = new Roo.Shadow({
22249             offset : this.shadowOffset,
22250             mode : config.shadow
22251         });
22252     }else{
22253         this.shadowOffset = 0;
22254     }
22255     this.useShim = config.shim !== false && Roo.useShims;
22256     this.useDisplay = config.useDisplay;
22257     this.hide();
22258 };
22259
22260 var supr = Roo.Element.prototype;
22261
22262 // shims are shared among layer to keep from having 100 iframes
22263 var shims = [];
22264
22265 Roo.extend(Roo.Layer, Roo.Element, {
22266
22267     getZIndex : function(){
22268         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
22269     },
22270
22271     getShim : function(){
22272         if(!this.useShim){
22273             return null;
22274         }
22275         if(this.shim){
22276             return this.shim;
22277         }
22278         var shim = shims.shift();
22279         if(!shim){
22280             shim = this.createShim();
22281             shim.enableDisplayMode('block');
22282             shim.dom.style.display = 'none';
22283             shim.dom.style.visibility = 'visible';
22284         }
22285         var pn = this.dom.parentNode;
22286         if(shim.dom.parentNode != pn){
22287             pn.insertBefore(shim.dom, this.dom);
22288         }
22289         shim.setStyle('z-index', this.getZIndex()-2);
22290         this.shim = shim;
22291         return shim;
22292     },
22293
22294     hideShim : function(){
22295         if(this.shim){
22296             this.shim.setDisplayed(false);
22297             shims.push(this.shim);
22298             delete this.shim;
22299         }
22300     },
22301
22302     disableShadow : function(){
22303         if(this.shadow){
22304             this.shadowDisabled = true;
22305             this.shadow.hide();
22306             this.lastShadowOffset = this.shadowOffset;
22307             this.shadowOffset = 0;
22308         }
22309     },
22310
22311     enableShadow : function(show){
22312         if(this.shadow){
22313             this.shadowDisabled = false;
22314             this.shadowOffset = this.lastShadowOffset;
22315             delete this.lastShadowOffset;
22316             if(show){
22317                 this.sync(true);
22318             }
22319         }
22320     },
22321
22322     // private
22323     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
22324     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
22325     sync : function(doShow){
22326         var sw = this.shadow;
22327         if(!this.updating && this.isVisible() && (sw || this.useShim)){
22328             var sh = this.getShim();
22329
22330             var w = this.getWidth(),
22331                 h = this.getHeight();
22332
22333             var l = this.getLeft(true),
22334                 t = this.getTop(true);
22335
22336             if(sw && !this.shadowDisabled){
22337                 if(doShow && !sw.isVisible()){
22338                     sw.show(this);
22339                 }else{
22340                     sw.realign(l, t, w, h);
22341                 }
22342                 if(sh){
22343                     if(doShow){
22344                        sh.show();
22345                     }
22346                     // fit the shim behind the shadow, so it is shimmed too
22347                     var a = sw.adjusts, s = sh.dom.style;
22348                     s.left = (Math.min(l, l+a.l))+"px";
22349                     s.top = (Math.min(t, t+a.t))+"px";
22350                     s.width = (w+a.w)+"px";
22351                     s.height = (h+a.h)+"px";
22352                 }
22353             }else if(sh){
22354                 if(doShow){
22355                    sh.show();
22356                 }
22357                 sh.setSize(w, h);
22358                 sh.setLeftTop(l, t);
22359             }
22360             
22361         }
22362     },
22363
22364     // private
22365     destroy : function(){
22366         this.hideShim();
22367         if(this.shadow){
22368             this.shadow.hide();
22369         }
22370         this.removeAllListeners();
22371         var pn = this.dom.parentNode;
22372         if(pn){
22373             pn.removeChild(this.dom);
22374         }
22375         Roo.Element.uncache(this.id);
22376     },
22377
22378     remove : function(){
22379         this.destroy();
22380     },
22381
22382     // private
22383     beginUpdate : function(){
22384         this.updating = true;
22385     },
22386
22387     // private
22388     endUpdate : function(){
22389         this.updating = false;
22390         this.sync(true);
22391     },
22392
22393     // private
22394     hideUnders : function(negOffset){
22395         if(this.shadow){
22396             this.shadow.hide();
22397         }
22398         this.hideShim();
22399     },
22400
22401     // private
22402     constrainXY : function(){
22403         if(this.constrain){
22404             var vw = Roo.lib.Dom.getViewWidth(),
22405                 vh = Roo.lib.Dom.getViewHeight();
22406             var s = Roo.get(document).getScroll();
22407
22408             var xy = this.getXY();
22409             var x = xy[0], y = xy[1];   
22410             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
22411             // only move it if it needs it
22412             var moved = false;
22413             // first validate right/bottom
22414             if((x + w) > vw+s.left){
22415                 x = vw - w - this.shadowOffset;
22416                 moved = true;
22417             }
22418             if((y + h) > vh+s.top){
22419                 y = vh - h - this.shadowOffset;
22420                 moved = true;
22421             }
22422             // then make sure top/left isn't negative
22423             if(x < s.left){
22424                 x = s.left;
22425                 moved = true;
22426             }
22427             if(y < s.top){
22428                 y = s.top;
22429                 moved = true;
22430             }
22431             if(moved){
22432                 if(this.avoidY){
22433                     var ay = this.avoidY;
22434                     if(y <= ay && (y+h) >= ay){
22435                         y = ay-h-5;   
22436                     }
22437                 }
22438                 xy = [x, y];
22439                 this.storeXY(xy);
22440                 supr.setXY.call(this, xy);
22441                 this.sync();
22442             }
22443         }
22444     },
22445
22446     isVisible : function(){
22447         return this.visible;    
22448     },
22449
22450     // private
22451     showAction : function(){
22452         this.visible = true; // track visibility to prevent getStyle calls
22453         if(this.useDisplay === true){
22454             this.setDisplayed("");
22455         }else if(this.lastXY){
22456             supr.setXY.call(this, this.lastXY);
22457         }else if(this.lastLT){
22458             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
22459         }
22460     },
22461
22462     // private
22463     hideAction : function(){
22464         this.visible = false;
22465         if(this.useDisplay === true){
22466             this.setDisplayed(false);
22467         }else{
22468             this.setLeftTop(-10000,-10000);
22469         }
22470     },
22471
22472     // overridden Element method
22473     setVisible : function(v, a, d, c, e){
22474         if(v){
22475             this.showAction();
22476         }
22477         if(a && v){
22478             var cb = function(){
22479                 this.sync(true);
22480                 if(c){
22481                     c();
22482                 }
22483             }.createDelegate(this);
22484             supr.setVisible.call(this, true, true, d, cb, e);
22485         }else{
22486             if(!v){
22487                 this.hideUnders(true);
22488             }
22489             var cb = c;
22490             if(a){
22491                 cb = function(){
22492                     this.hideAction();
22493                     if(c){
22494                         c();
22495                     }
22496                 }.createDelegate(this);
22497             }
22498             supr.setVisible.call(this, v, a, d, cb, e);
22499             if(v){
22500                 this.sync(true);
22501             }else if(!a){
22502                 this.hideAction();
22503             }
22504         }
22505     },
22506
22507     storeXY : function(xy){
22508         delete this.lastLT;
22509         this.lastXY = xy;
22510     },
22511
22512     storeLeftTop : function(left, top){
22513         delete this.lastXY;
22514         this.lastLT = [left, top];
22515     },
22516
22517     // private
22518     beforeFx : function(){
22519         this.beforeAction();
22520         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
22521     },
22522
22523     // private
22524     afterFx : function(){
22525         Roo.Layer.superclass.afterFx.apply(this, arguments);
22526         this.sync(this.isVisible());
22527     },
22528
22529     // private
22530     beforeAction : function(){
22531         if(!this.updating && this.shadow){
22532             this.shadow.hide();
22533         }
22534     },
22535
22536     // overridden Element method
22537     setLeft : function(left){
22538         this.storeLeftTop(left, this.getTop(true));
22539         supr.setLeft.apply(this, arguments);
22540         this.sync();
22541     },
22542
22543     setTop : function(top){
22544         this.storeLeftTop(this.getLeft(true), top);
22545         supr.setTop.apply(this, arguments);
22546         this.sync();
22547     },
22548
22549     setLeftTop : function(left, top){
22550         this.storeLeftTop(left, top);
22551         supr.setLeftTop.apply(this, arguments);
22552         this.sync();
22553     },
22554
22555     setXY : function(xy, a, d, c, e){
22556         this.fixDisplay();
22557         this.beforeAction();
22558         this.storeXY(xy);
22559         var cb = this.createCB(c);
22560         supr.setXY.call(this, xy, a, d, cb, e);
22561         if(!a){
22562             cb();
22563         }
22564     },
22565
22566     // private
22567     createCB : function(c){
22568         var el = this;
22569         return function(){
22570             el.constrainXY();
22571             el.sync(true);
22572             if(c){
22573                 c();
22574             }
22575         };
22576     },
22577
22578     // overridden Element method
22579     setX : function(x, a, d, c, e){
22580         this.setXY([x, this.getY()], a, d, c, e);
22581     },
22582
22583     // overridden Element method
22584     setY : function(y, a, d, c, e){
22585         this.setXY([this.getX(), y], a, d, c, e);
22586     },
22587
22588     // overridden Element method
22589     setSize : function(w, h, a, d, c, e){
22590         this.beforeAction();
22591         var cb = this.createCB(c);
22592         supr.setSize.call(this, w, h, a, d, cb, e);
22593         if(!a){
22594             cb();
22595         }
22596     },
22597
22598     // overridden Element method
22599     setWidth : function(w, a, d, c, e){
22600         this.beforeAction();
22601         var cb = this.createCB(c);
22602         supr.setWidth.call(this, w, a, d, cb, e);
22603         if(!a){
22604             cb();
22605         }
22606     },
22607
22608     // overridden Element method
22609     setHeight : function(h, a, d, c, e){
22610         this.beforeAction();
22611         var cb = this.createCB(c);
22612         supr.setHeight.call(this, h, a, d, cb, e);
22613         if(!a){
22614             cb();
22615         }
22616     },
22617
22618     // overridden Element method
22619     setBounds : function(x, y, w, h, a, d, c, e){
22620         this.beforeAction();
22621         var cb = this.createCB(c);
22622         if(!a){
22623             this.storeXY([x, y]);
22624             supr.setXY.call(this, [x, y]);
22625             supr.setSize.call(this, w, h, a, d, cb, e);
22626             cb();
22627         }else{
22628             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
22629         }
22630         return this;
22631     },
22632     
22633     /**
22634      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
22635      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
22636      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
22637      * @param {Number} zindex The new z-index to set
22638      * @return {this} The Layer
22639      */
22640     setZIndex : function(zindex){
22641         this.zindex = zindex;
22642         this.setStyle("z-index", zindex + 2);
22643         if(this.shadow){
22644             this.shadow.setZIndex(zindex + 1);
22645         }
22646         if(this.shim){
22647             this.shim.setStyle("z-index", zindex);
22648         }
22649     }
22650 });
22651 })();/*
22652  * Based on:
22653  * Ext JS Library 1.1.1
22654  * Copyright(c) 2006-2007, Ext JS, LLC.
22655  *
22656  * Originally Released Under LGPL - original licence link has changed is not relivant.
22657  *
22658  * Fork - LGPL
22659  * <script type="text/javascript">
22660  */
22661
22662
22663 /**
22664  * @class Roo.Shadow
22665  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
22666  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
22667  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
22668  * @constructor
22669  * Create a new Shadow
22670  * @param {Object} config The config object
22671  */
22672 Roo.Shadow = function(config){
22673     Roo.apply(this, config);
22674     if(typeof this.mode != "string"){
22675         this.mode = this.defaultMode;
22676     }
22677     var o = this.offset, a = {h: 0};
22678     var rad = Math.floor(this.offset/2);
22679     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
22680         case "drop":
22681             a.w = 0;
22682             a.l = a.t = o;
22683             a.t -= 1;
22684             if(Roo.isIE){
22685                 a.l -= this.offset + rad;
22686                 a.t -= this.offset + rad;
22687                 a.w -= rad;
22688                 a.h -= rad;
22689                 a.t += 1;
22690             }
22691         break;
22692         case "sides":
22693             a.w = (o*2);
22694             a.l = -o;
22695             a.t = o-1;
22696             if(Roo.isIE){
22697                 a.l -= (this.offset - rad);
22698                 a.t -= this.offset + rad;
22699                 a.l += 1;
22700                 a.w -= (this.offset - rad)*2;
22701                 a.w -= rad + 1;
22702                 a.h -= 1;
22703             }
22704         break;
22705         case "frame":
22706             a.w = a.h = (o*2);
22707             a.l = a.t = -o;
22708             a.t += 1;
22709             a.h -= 2;
22710             if(Roo.isIE){
22711                 a.l -= (this.offset - rad);
22712                 a.t -= (this.offset - rad);
22713                 a.l += 1;
22714                 a.w -= (this.offset + rad + 1);
22715                 a.h -= (this.offset + rad);
22716                 a.h += 1;
22717             }
22718         break;
22719     };
22720
22721     this.adjusts = a;
22722 };
22723
22724 Roo.Shadow.prototype = {
22725     /**
22726      * @cfg {String} mode
22727      * The shadow display mode.  Supports the following options:<br />
22728      * sides: Shadow displays on both sides and bottom only<br />
22729      * frame: Shadow displays equally on all four sides<br />
22730      * drop: Traditional bottom-right drop shadow (default)
22731      */
22732     /**
22733      * @cfg {String} offset
22734      * The number of pixels to offset the shadow from the element (defaults to 4)
22735      */
22736     offset: 4,
22737
22738     // private
22739     defaultMode: "drop",
22740
22741     /**
22742      * Displays the shadow under the target element
22743      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
22744      */
22745     show : function(target){
22746         target = Roo.get(target);
22747         if(!this.el){
22748             this.el = Roo.Shadow.Pool.pull();
22749             if(this.el.dom.nextSibling != target.dom){
22750                 this.el.insertBefore(target);
22751             }
22752         }
22753         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
22754         if(Roo.isIE){
22755             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
22756         }
22757         this.realign(
22758             target.getLeft(true),
22759             target.getTop(true),
22760             target.getWidth(),
22761             target.getHeight()
22762         );
22763         this.el.dom.style.display = "block";
22764     },
22765
22766     /**
22767      * Returns true if the shadow is visible, else false
22768      */
22769     isVisible : function(){
22770         return this.el ? true : false;  
22771     },
22772
22773     /**
22774      * Direct alignment when values are already available. Show must be called at least once before
22775      * calling this method to ensure it is initialized.
22776      * @param {Number} left The target element left position
22777      * @param {Number} top The target element top position
22778      * @param {Number} width The target element width
22779      * @param {Number} height The target element height
22780      */
22781     realign : function(l, t, w, h){
22782         if(!this.el){
22783             return;
22784         }
22785         var a = this.adjusts, d = this.el.dom, s = d.style;
22786         var iea = 0;
22787         s.left = (l+a.l)+"px";
22788         s.top = (t+a.t)+"px";
22789         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
22790  
22791         if(s.width != sws || s.height != shs){
22792             s.width = sws;
22793             s.height = shs;
22794             if(!Roo.isIE){
22795                 var cn = d.childNodes;
22796                 var sww = Math.max(0, (sw-12))+"px";
22797                 cn[0].childNodes[1].style.width = sww;
22798                 cn[1].childNodes[1].style.width = sww;
22799                 cn[2].childNodes[1].style.width = sww;
22800                 cn[1].style.height = Math.max(0, (sh-12))+"px";
22801             }
22802         }
22803     },
22804
22805     /**
22806      * Hides this shadow
22807      */
22808     hide : function(){
22809         if(this.el){
22810             this.el.dom.style.display = "none";
22811             Roo.Shadow.Pool.push(this.el);
22812             delete this.el;
22813         }
22814     },
22815
22816     /**
22817      * Adjust the z-index of this shadow
22818      * @param {Number} zindex The new z-index
22819      */
22820     setZIndex : function(z){
22821         this.zIndex = z;
22822         if(this.el){
22823             this.el.setStyle("z-index", z);
22824         }
22825     }
22826 };
22827
22828 // Private utility class that manages the internal Shadow cache
22829 Roo.Shadow.Pool = function(){
22830     var p = [];
22831     var markup = Roo.isIE ?
22832                  '<div class="x-ie-shadow"></div>' :
22833                  '<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>';
22834     return {
22835         pull : function(){
22836             var sh = p.shift();
22837             if(!sh){
22838                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
22839                 sh.autoBoxAdjust = false;
22840             }
22841             return sh;
22842         },
22843
22844         push : function(sh){
22845             p.push(sh);
22846         }
22847     };
22848 }();/*
22849  * Based on:
22850  * Ext JS Library 1.1.1
22851  * Copyright(c) 2006-2007, Ext JS, LLC.
22852  *
22853  * Originally Released Under LGPL - original licence link has changed is not relivant.
22854  *
22855  * Fork - LGPL
22856  * <script type="text/javascript">
22857  */
22858
22859 /**
22860  * @class Roo.BoxComponent
22861  * @extends Roo.Component
22862  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
22863  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
22864  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
22865  * layout containers.
22866  * @constructor
22867  * @param {Roo.Element/String/Object} config The configuration options.
22868  */
22869 Roo.BoxComponent = function(config){
22870     Roo.Component.call(this, config);
22871     this.addEvents({
22872         /**
22873          * @event resize
22874          * Fires after the component is resized.
22875              * @param {Roo.Component} this
22876              * @param {Number} adjWidth The box-adjusted width that was set
22877              * @param {Number} adjHeight The box-adjusted height that was set
22878              * @param {Number} rawWidth The width that was originally specified
22879              * @param {Number} rawHeight The height that was originally specified
22880              */
22881         resize : true,
22882         /**
22883          * @event move
22884          * Fires after the component is moved.
22885              * @param {Roo.Component} this
22886              * @param {Number} x The new x position
22887              * @param {Number} y The new y position
22888              */
22889         move : true
22890     });
22891 };
22892
22893 Roo.extend(Roo.BoxComponent, Roo.Component, {
22894     // private, set in afterRender to signify that the component has been rendered
22895     boxReady : false,
22896     // private, used to defer height settings to subclasses
22897     deferHeight: false,
22898     /** @cfg {Number} width
22899      * width (optional) size of component
22900      */
22901      /** @cfg {Number} height
22902      * height (optional) size of component
22903      */
22904      
22905     /**
22906      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
22907      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
22908      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
22909      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
22910      * @return {Roo.BoxComponent} this
22911      */
22912     setSize : function(w, h){
22913         // support for standard size objects
22914         if(typeof w == 'object'){
22915             h = w.height;
22916             w = w.width;
22917         }
22918         // not rendered
22919         if(!this.boxReady){
22920             this.width = w;
22921             this.height = h;
22922             return this;
22923         }
22924
22925         // prevent recalcs when not needed
22926         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
22927             return this;
22928         }
22929         this.lastSize = {width: w, height: h};
22930
22931         var adj = this.adjustSize(w, h);
22932         var aw = adj.width, ah = adj.height;
22933         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
22934             var rz = this.getResizeEl();
22935             if(!this.deferHeight && aw !== undefined && ah !== undefined){
22936                 rz.setSize(aw, ah);
22937             }else if(!this.deferHeight && ah !== undefined){
22938                 rz.setHeight(ah);
22939             }else if(aw !== undefined){
22940                 rz.setWidth(aw);
22941             }
22942             this.onResize(aw, ah, w, h);
22943             this.fireEvent('resize', this, aw, ah, w, h);
22944         }
22945         return this;
22946     },
22947
22948     /**
22949      * Gets the current size of the component's underlying element.
22950      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
22951      */
22952     getSize : function(){
22953         return this.el.getSize();
22954     },
22955
22956     /**
22957      * Gets the current XY position of the component's underlying element.
22958      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
22959      * @return {Array} The XY position of the element (e.g., [100, 200])
22960      */
22961     getPosition : function(local){
22962         if(local === true){
22963             return [this.el.getLeft(true), this.el.getTop(true)];
22964         }
22965         return this.xy || this.el.getXY();
22966     },
22967
22968     /**
22969      * Gets the current box measurements of the component's underlying element.
22970      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
22971      * @returns {Object} box An object in the format {x, y, width, height}
22972      */
22973     getBox : function(local){
22974         var s = this.el.getSize();
22975         if(local){
22976             s.x = this.el.getLeft(true);
22977             s.y = this.el.getTop(true);
22978         }else{
22979             var xy = this.xy || this.el.getXY();
22980             s.x = xy[0];
22981             s.y = xy[1];
22982         }
22983         return s;
22984     },
22985
22986     /**
22987      * Sets the current box measurements of the component's underlying element.
22988      * @param {Object} box An object in the format {x, y, width, height}
22989      * @returns {Roo.BoxComponent} this
22990      */
22991     updateBox : function(box){
22992         this.setSize(box.width, box.height);
22993         this.setPagePosition(box.x, box.y);
22994         return this;
22995     },
22996
22997     // protected
22998     getResizeEl : function(){
22999         return this.resizeEl || this.el;
23000     },
23001
23002     // protected
23003     getPositionEl : function(){
23004         return this.positionEl || this.el;
23005     },
23006
23007     /**
23008      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
23009      * This method fires the move event.
23010      * @param {Number} left The new left
23011      * @param {Number} top The new top
23012      * @returns {Roo.BoxComponent} this
23013      */
23014     setPosition : function(x, y){
23015         this.x = x;
23016         this.y = y;
23017         if(!this.boxReady){
23018             return this;
23019         }
23020         var adj = this.adjustPosition(x, y);
23021         var ax = adj.x, ay = adj.y;
23022
23023         var el = this.getPositionEl();
23024         if(ax !== undefined || ay !== undefined){
23025             if(ax !== undefined && ay !== undefined){
23026                 el.setLeftTop(ax, ay);
23027             }else if(ax !== undefined){
23028                 el.setLeft(ax);
23029             }else if(ay !== undefined){
23030                 el.setTop(ay);
23031             }
23032             this.onPosition(ax, ay);
23033             this.fireEvent('move', this, ax, ay);
23034         }
23035         return this;
23036     },
23037
23038     /**
23039      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
23040      * This method fires the move event.
23041      * @param {Number} x The new x position
23042      * @param {Number} y The new y position
23043      * @returns {Roo.BoxComponent} this
23044      */
23045     setPagePosition : function(x, y){
23046         this.pageX = x;
23047         this.pageY = y;
23048         if(!this.boxReady){
23049             return;
23050         }
23051         if(x === undefined || y === undefined){ // cannot translate undefined points
23052             return;
23053         }
23054         var p = this.el.translatePoints(x, y);
23055         this.setPosition(p.left, p.top);
23056         return this;
23057     },
23058
23059     // private
23060     onRender : function(ct, position){
23061         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
23062         if(this.resizeEl){
23063             this.resizeEl = Roo.get(this.resizeEl);
23064         }
23065         if(this.positionEl){
23066             this.positionEl = Roo.get(this.positionEl);
23067         }
23068     },
23069
23070     // private
23071     afterRender : function(){
23072         Roo.BoxComponent.superclass.afterRender.call(this);
23073         this.boxReady = true;
23074         this.setSize(this.width, this.height);
23075         if(this.x || this.y){
23076             this.setPosition(this.x, this.y);
23077         }
23078         if(this.pageX || this.pageY){
23079             this.setPagePosition(this.pageX, this.pageY);
23080         }
23081     },
23082
23083     /**
23084      * Force the component's size to recalculate based on the underlying element's current height and width.
23085      * @returns {Roo.BoxComponent} this
23086      */
23087     syncSize : function(){
23088         delete this.lastSize;
23089         this.setSize(this.el.getWidth(), this.el.getHeight());
23090         return this;
23091     },
23092
23093     /**
23094      * Called after the component is resized, this method is empty by default but can be implemented by any
23095      * subclass that needs to perform custom logic after a resize occurs.
23096      * @param {Number} adjWidth The box-adjusted width that was set
23097      * @param {Number} adjHeight The box-adjusted height that was set
23098      * @param {Number} rawWidth The width that was originally specified
23099      * @param {Number} rawHeight The height that was originally specified
23100      */
23101     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
23102
23103     },
23104
23105     /**
23106      * Called after the component is moved, this method is empty by default but can be implemented by any
23107      * subclass that needs to perform custom logic after a move occurs.
23108      * @param {Number} x The new x position
23109      * @param {Number} y The new y position
23110      */
23111     onPosition : function(x, y){
23112
23113     },
23114
23115     // private
23116     adjustSize : function(w, h){
23117         if(this.autoWidth){
23118             w = 'auto';
23119         }
23120         if(this.autoHeight){
23121             h = 'auto';
23122         }
23123         return {width : w, height: h};
23124     },
23125
23126     // private
23127     adjustPosition : function(x, y){
23128         return {x : x, y: y};
23129     }
23130 });/*
23131  * Based on:
23132  * Ext JS Library 1.1.1
23133  * Copyright(c) 2006-2007, Ext JS, LLC.
23134  *
23135  * Originally Released Under LGPL - original licence link has changed is not relivant.
23136  *
23137  * Fork - LGPL
23138  * <script type="text/javascript">
23139  */
23140
23141
23142 /**
23143  * @class Roo.SplitBar
23144  * @extends Roo.util.Observable
23145  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
23146  * <br><br>
23147  * Usage:
23148  * <pre><code>
23149 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
23150                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
23151 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
23152 split.minSize = 100;
23153 split.maxSize = 600;
23154 split.animate = true;
23155 split.on('moved', splitterMoved);
23156 </code></pre>
23157  * @constructor
23158  * Create a new SplitBar
23159  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
23160  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
23161  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
23162  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
23163                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
23164                         position of the SplitBar).
23165  */
23166 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
23167     
23168     /** @private */
23169     this.el = Roo.get(dragElement, true);
23170     this.el.dom.unselectable = "on";
23171     /** @private */
23172     this.resizingEl = Roo.get(resizingElement, true);
23173
23174     /**
23175      * @private
23176      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
23177      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
23178      * @type Number
23179      */
23180     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
23181     
23182     /**
23183      * The minimum size of the resizing element. (Defaults to 0)
23184      * @type Number
23185      */
23186     this.minSize = 0;
23187     
23188     /**
23189      * The maximum size of the resizing element. (Defaults to 2000)
23190      * @type Number
23191      */
23192     this.maxSize = 2000;
23193     
23194     /**
23195      * Whether to animate the transition to the new size
23196      * @type Boolean
23197      */
23198     this.animate = false;
23199     
23200     /**
23201      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
23202      * @type Boolean
23203      */
23204     this.useShim = false;
23205     
23206     /** @private */
23207     this.shim = null;
23208     
23209     if(!existingProxy){
23210         /** @private */
23211         this.proxy = Roo.SplitBar.createProxy(this.orientation);
23212     }else{
23213         this.proxy = Roo.get(existingProxy).dom;
23214     }
23215     /** @private */
23216     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
23217     
23218     /** @private */
23219     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
23220     
23221     /** @private */
23222     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
23223     
23224     /** @private */
23225     this.dragSpecs = {};
23226     
23227     /**
23228      * @private The adapter to use to positon and resize elements
23229      */
23230     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
23231     this.adapter.init(this);
23232     
23233     if(this.orientation == Roo.SplitBar.HORIZONTAL){
23234         /** @private */
23235         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
23236         this.el.addClass("x-splitbar-h");
23237     }else{
23238         /** @private */
23239         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
23240         this.el.addClass("x-splitbar-v");
23241     }
23242     
23243     this.addEvents({
23244         /**
23245          * @event resize
23246          * Fires when the splitter is moved (alias for {@link #event-moved})
23247          * @param {Roo.SplitBar} this
23248          * @param {Number} newSize the new width or height
23249          */
23250         "resize" : true,
23251         /**
23252          * @event moved
23253          * Fires when the splitter is moved
23254          * @param {Roo.SplitBar} this
23255          * @param {Number} newSize the new width or height
23256          */
23257         "moved" : true,
23258         /**
23259          * @event beforeresize
23260          * Fires before the splitter is dragged
23261          * @param {Roo.SplitBar} this
23262          */
23263         "beforeresize" : true,
23264
23265         "beforeapply" : true
23266     });
23267
23268     Roo.util.Observable.call(this);
23269 };
23270
23271 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
23272     onStartProxyDrag : function(x, y){
23273         this.fireEvent("beforeresize", this);
23274         if(!this.overlay){
23275             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
23276             o.unselectable();
23277             o.enableDisplayMode("block");
23278             // all splitbars share the same overlay
23279             Roo.SplitBar.prototype.overlay = o;
23280         }
23281         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
23282         this.overlay.show();
23283         Roo.get(this.proxy).setDisplayed("block");
23284         var size = this.adapter.getElementSize(this);
23285         this.activeMinSize = this.getMinimumSize();;
23286         this.activeMaxSize = this.getMaximumSize();;
23287         var c1 = size - this.activeMinSize;
23288         var c2 = Math.max(this.activeMaxSize - size, 0);
23289         if(this.orientation == Roo.SplitBar.HORIZONTAL){
23290             this.dd.resetConstraints();
23291             this.dd.setXConstraint(
23292                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
23293                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
23294             );
23295             this.dd.setYConstraint(0, 0);
23296         }else{
23297             this.dd.resetConstraints();
23298             this.dd.setXConstraint(0, 0);
23299             this.dd.setYConstraint(
23300                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
23301                 this.placement == Roo.SplitBar.TOP ? c2 : c1
23302             );
23303          }
23304         this.dragSpecs.startSize = size;
23305         this.dragSpecs.startPoint = [x, y];
23306         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
23307     },
23308     
23309     /** 
23310      * @private Called after the drag operation by the DDProxy
23311      */
23312     onEndProxyDrag : function(e){
23313         Roo.get(this.proxy).setDisplayed(false);
23314         var endPoint = Roo.lib.Event.getXY(e);
23315         if(this.overlay){
23316             this.overlay.hide();
23317         }
23318         var newSize;
23319         if(this.orientation == Roo.SplitBar.HORIZONTAL){
23320             newSize = this.dragSpecs.startSize + 
23321                 (this.placement == Roo.SplitBar.LEFT ?
23322                     endPoint[0] - this.dragSpecs.startPoint[0] :
23323                     this.dragSpecs.startPoint[0] - endPoint[0]
23324                 );
23325         }else{
23326             newSize = this.dragSpecs.startSize + 
23327                 (this.placement == Roo.SplitBar.TOP ?
23328                     endPoint[1] - this.dragSpecs.startPoint[1] :
23329                     this.dragSpecs.startPoint[1] - endPoint[1]
23330                 );
23331         }
23332         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
23333         if(newSize != this.dragSpecs.startSize){
23334             if(this.fireEvent('beforeapply', this, newSize) !== false){
23335                 this.adapter.setElementSize(this, newSize);
23336                 this.fireEvent("moved", this, newSize);
23337                 this.fireEvent("resize", this, newSize);
23338             }
23339         }
23340     },
23341     
23342     /**
23343      * Get the adapter this SplitBar uses
23344      * @return The adapter object
23345      */
23346     getAdapter : function(){
23347         return this.adapter;
23348     },
23349     
23350     /**
23351      * Set the adapter this SplitBar uses
23352      * @param {Object} adapter A SplitBar adapter object
23353      */
23354     setAdapter : function(adapter){
23355         this.adapter = adapter;
23356         this.adapter.init(this);
23357     },
23358     
23359     /**
23360      * Gets the minimum size for the resizing element
23361      * @return {Number} The minimum size
23362      */
23363     getMinimumSize : function(){
23364         return this.minSize;
23365     },
23366     
23367     /**
23368      * Sets the minimum size for the resizing element
23369      * @param {Number} minSize The minimum size
23370      */
23371     setMinimumSize : function(minSize){
23372         this.minSize = minSize;
23373     },
23374     
23375     /**
23376      * Gets the maximum size for the resizing element
23377      * @return {Number} The maximum size
23378      */
23379     getMaximumSize : function(){
23380         return this.maxSize;
23381     },
23382     
23383     /**
23384      * Sets the maximum size for the resizing element
23385      * @param {Number} maxSize The maximum size
23386      */
23387     setMaximumSize : function(maxSize){
23388         this.maxSize = maxSize;
23389     },
23390     
23391     /**
23392      * Sets the initialize size for the resizing element
23393      * @param {Number} size The initial size
23394      */
23395     setCurrentSize : function(size){
23396         var oldAnimate = this.animate;
23397         this.animate = false;
23398         this.adapter.setElementSize(this, size);
23399         this.animate = oldAnimate;
23400     },
23401     
23402     /**
23403      * Destroy this splitbar. 
23404      * @param {Boolean} removeEl True to remove the element
23405      */
23406     destroy : function(removeEl){
23407         if(this.shim){
23408             this.shim.remove();
23409         }
23410         this.dd.unreg();
23411         this.proxy.parentNode.removeChild(this.proxy);
23412         if(removeEl){
23413             this.el.remove();
23414         }
23415     }
23416 });
23417
23418 /**
23419  * @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.
23420  */
23421 Roo.SplitBar.createProxy = function(dir){
23422     var proxy = new Roo.Element(document.createElement("div"));
23423     proxy.unselectable();
23424     var cls = 'x-splitbar-proxy';
23425     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
23426     document.body.appendChild(proxy.dom);
23427     return proxy.dom;
23428 };
23429
23430 /** 
23431  * @class Roo.SplitBar.BasicLayoutAdapter
23432  * Default Adapter. It assumes the splitter and resizing element are not positioned
23433  * elements and only gets/sets the width of the element. Generally used for table based layouts.
23434  */
23435 Roo.SplitBar.BasicLayoutAdapter = function(){
23436 };
23437
23438 Roo.SplitBar.BasicLayoutAdapter.prototype = {
23439     // do nothing for now
23440     init : function(s){
23441     
23442     },
23443     /**
23444      * Called before drag operations to get the current size of the resizing element. 
23445      * @param {Roo.SplitBar} s The SplitBar using this adapter
23446      */
23447      getElementSize : function(s){
23448         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23449             return s.resizingEl.getWidth();
23450         }else{
23451             return s.resizingEl.getHeight();
23452         }
23453     },
23454     
23455     /**
23456      * Called after drag operations to set the size of the resizing element.
23457      * @param {Roo.SplitBar} s The SplitBar using this adapter
23458      * @param {Number} newSize The new size to set
23459      * @param {Function} onComplete A function to be invoked when resizing is complete
23460      */
23461     setElementSize : function(s, newSize, onComplete){
23462         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23463             if(!s.animate){
23464                 s.resizingEl.setWidth(newSize);
23465                 if(onComplete){
23466                     onComplete(s, newSize);
23467                 }
23468             }else{
23469                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
23470             }
23471         }else{
23472             
23473             if(!s.animate){
23474                 s.resizingEl.setHeight(newSize);
23475                 if(onComplete){
23476                     onComplete(s, newSize);
23477                 }
23478             }else{
23479                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
23480             }
23481         }
23482     }
23483 };
23484
23485 /** 
23486  *@class Roo.SplitBar.AbsoluteLayoutAdapter
23487  * @extends Roo.SplitBar.BasicLayoutAdapter
23488  * Adapter that  moves the splitter element to align with the resized sizing element. 
23489  * Used with an absolute positioned SplitBar.
23490  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
23491  * document.body, make sure you assign an id to the body element.
23492  */
23493 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
23494     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
23495     this.container = Roo.get(container);
23496 };
23497
23498 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
23499     init : function(s){
23500         this.basic.init(s);
23501     },
23502     
23503     getElementSize : function(s){
23504         return this.basic.getElementSize(s);
23505     },
23506     
23507     setElementSize : function(s, newSize, onComplete){
23508         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
23509     },
23510     
23511     moveSplitter : function(s){
23512         var yes = Roo.SplitBar;
23513         switch(s.placement){
23514             case yes.LEFT:
23515                 s.el.setX(s.resizingEl.getRight());
23516                 break;
23517             case yes.RIGHT:
23518                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
23519                 break;
23520             case yes.TOP:
23521                 s.el.setY(s.resizingEl.getBottom());
23522                 break;
23523             case yes.BOTTOM:
23524                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
23525                 break;
23526         }
23527     }
23528 };
23529
23530 /**
23531  * Orientation constant - Create a vertical SplitBar
23532  * @static
23533  * @type Number
23534  */
23535 Roo.SplitBar.VERTICAL = 1;
23536
23537 /**
23538  * Orientation constant - Create a horizontal SplitBar
23539  * @static
23540  * @type Number
23541  */
23542 Roo.SplitBar.HORIZONTAL = 2;
23543
23544 /**
23545  * Placement constant - The resizing element is to the left of the splitter element
23546  * @static
23547  * @type Number
23548  */
23549 Roo.SplitBar.LEFT = 1;
23550
23551 /**
23552  * Placement constant - The resizing element is to the right of the splitter element
23553  * @static
23554  * @type Number
23555  */
23556 Roo.SplitBar.RIGHT = 2;
23557
23558 /**
23559  * Placement constant - The resizing element is positioned above the splitter element
23560  * @static
23561  * @type Number
23562  */
23563 Roo.SplitBar.TOP = 3;
23564
23565 /**
23566  * Placement constant - The resizing element is positioned under splitter element
23567  * @static
23568  * @type Number
23569  */
23570 Roo.SplitBar.BOTTOM = 4;
23571 /*
23572  * Based on:
23573  * Ext JS Library 1.1.1
23574  * Copyright(c) 2006-2007, Ext JS, LLC.
23575  *
23576  * Originally Released Under LGPL - original licence link has changed is not relivant.
23577  *
23578  * Fork - LGPL
23579  * <script type="text/javascript">
23580  */
23581
23582 /**
23583  * @class Roo.View
23584  * @extends Roo.util.Observable
23585  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
23586  * This class also supports single and multi selection modes. <br>
23587  * Create a data model bound view:
23588  <pre><code>
23589  var store = new Roo.data.Store(...);
23590
23591  var view = new Roo.View({
23592     el : "my-element",
23593     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
23594  
23595     singleSelect: true,
23596     selectedClass: "ydataview-selected",
23597     store: store
23598  });
23599
23600  // listen for node click?
23601  view.on("click", function(vw, index, node, e){
23602  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
23603  });
23604
23605  // load XML data
23606  dataModel.load("foobar.xml");
23607  </code></pre>
23608  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
23609  * <br><br>
23610  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
23611  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
23612  * 
23613  * Note: old style constructor is still suported (container, template, config)
23614  * 
23615  * @constructor
23616  * Create a new View
23617  * @param {Object} config The config object
23618  * 
23619  */
23620 Roo.View = function(config, depreciated_tpl, depreciated_config){
23621     
23622     if (typeof(depreciated_tpl) == 'undefined') {
23623         // new way.. - universal constructor.
23624         Roo.apply(this, config);
23625         this.el  = Roo.get(this.el);
23626     } else {
23627         // old format..
23628         this.el  = Roo.get(config);
23629         this.tpl = depreciated_tpl;
23630         Roo.apply(this, depreciated_config);
23631     }
23632      
23633     
23634     if(typeof(this.tpl) == "string"){
23635         this.tpl = new Roo.Template(this.tpl);
23636     } else {
23637         // support xtype ctors..
23638         this.tpl = new Roo.factory(this.tpl, Roo);
23639     }
23640     
23641     
23642     this.tpl.compile();
23643    
23644
23645      
23646     /** @private */
23647     this.addEvents({
23648         /**
23649          * @event beforeclick
23650          * Fires before a click is processed. Returns false to cancel the default action.
23651          * @param {Roo.View} this
23652          * @param {Number} index The index of the target node
23653          * @param {HTMLElement} node The target node
23654          * @param {Roo.EventObject} e The raw event object
23655          */
23656             "beforeclick" : true,
23657         /**
23658          * @event click
23659          * Fires when a template node is clicked.
23660          * @param {Roo.View} this
23661          * @param {Number} index The index of the target node
23662          * @param {HTMLElement} node The target node
23663          * @param {Roo.EventObject} e The raw event object
23664          */
23665             "click" : true,
23666         /**
23667          * @event dblclick
23668          * Fires when a template node is double clicked.
23669          * @param {Roo.View} this
23670          * @param {Number} index The index of the target node
23671          * @param {HTMLElement} node The target node
23672          * @param {Roo.EventObject} e The raw event object
23673          */
23674             "dblclick" : true,
23675         /**
23676          * @event contextmenu
23677          * Fires when a template node is right clicked.
23678          * @param {Roo.View} this
23679          * @param {Number} index The index of the target node
23680          * @param {HTMLElement} node The target node
23681          * @param {Roo.EventObject} e The raw event object
23682          */
23683             "contextmenu" : true,
23684         /**
23685          * @event selectionchange
23686          * Fires when the selected nodes change.
23687          * @param {Roo.View} this
23688          * @param {Array} selections Array of the selected nodes
23689          */
23690             "selectionchange" : true,
23691     
23692         /**
23693          * @event beforeselect
23694          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
23695          * @param {Roo.View} this
23696          * @param {HTMLElement} node The node to be selected
23697          * @param {Array} selections Array of currently selected nodes
23698          */
23699             "beforeselect" : true,
23700         /**
23701          * @event preparedata
23702          * Fires on every row to render, to allow you to change the data.
23703          * @param {Roo.View} this
23704          * @param {Object} data to be rendered (change this)
23705          */
23706           "preparedata" : true
23707         });
23708
23709     this.el.on({
23710         "click": this.onClick,
23711         "dblclick": this.onDblClick,
23712         "contextmenu": this.onContextMenu,
23713         scope:this
23714     });
23715
23716     this.selections = [];
23717     this.nodes = [];
23718     this.cmp = new Roo.CompositeElementLite([]);
23719     if(this.store){
23720         this.store = Roo.factory(this.store, Roo.data);
23721         this.setStore(this.store, true);
23722     }
23723     Roo.View.superclass.constructor.call(this);
23724 };
23725
23726 Roo.extend(Roo.View, Roo.util.Observable, {
23727     
23728      /**
23729      * @cfg {Roo.data.Store} store Data store to load data from.
23730      */
23731     store : false,
23732     
23733     /**
23734      * @cfg {String|Roo.Element} el The container element.
23735      */
23736     el : '',
23737     
23738     /**
23739      * @cfg {String|Roo.Template} tpl The template used by this View 
23740      */
23741     tpl : false,
23742     
23743     /**
23744      * @cfg {String} selectedClass The css class to add to selected nodes
23745      */
23746     selectedClass : "x-view-selected",
23747      /**
23748      * @cfg {String} emptyText The empty text to show when nothing is loaded.
23749      */
23750     emptyText : "",
23751     /**
23752      * @cfg {Boolean} multiSelect Allow multiple selection
23753      */
23754     multiSelect : false,
23755     /**
23756      * @cfg {Boolean} singleSelect Allow single selection
23757      */
23758     singleSelect:  false,
23759     
23760     /**
23761      * @cfg {Boolean} toggleSelect - selecting 
23762      */
23763     toggleSelect : false,
23764     
23765     /**
23766      * Returns the element this view is bound to.
23767      * @return {Roo.Element}
23768      */
23769     getEl : function(){
23770         return this.el;
23771     },
23772
23773     /**
23774      * Refreshes the view.
23775      */
23776     refresh : function(){
23777         var t = this.tpl;
23778         this.clearSelections();
23779         this.el.update("");
23780         var html = [];
23781         var records = this.store.getRange();
23782         if(records.length < 1){
23783             this.el.update(this.emptyText);
23784             return;
23785         }
23786         for(var i = 0, len = records.length; i < len; i++){
23787             var data = this.prepareData(records[i].data, i, records[i]);
23788             this.fireEvent("preparedata", this, data, i, records[i]);
23789             html[html.length] = t.apply(data);
23790         }
23791         this.el.update(html.join(""));
23792         this.nodes = this.el.dom.childNodes;
23793         this.updateIndexes(0);
23794     },
23795
23796     /**
23797      * Function to override to reformat the data that is sent to
23798      * the template for each node.
23799      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
23800      * a JSON object for an UpdateManager bound view).
23801      */
23802     prepareData : function(data){
23803         return data;
23804     },
23805
23806     onUpdate : function(ds, record){
23807         this.clearSelections();
23808         var index = this.store.indexOf(record);
23809         var n = this.nodes[index];
23810         this.tpl.insertBefore(n, this.prepareData(record.data));
23811         n.parentNode.removeChild(n);
23812         this.updateIndexes(index, index);
23813     },
23814
23815     onAdd : function(ds, records, index){
23816         this.clearSelections();
23817         if(this.nodes.length == 0){
23818             this.refresh();
23819             return;
23820         }
23821         var n = this.nodes[index];
23822         for(var i = 0, len = records.length; i < len; i++){
23823             var d = this.prepareData(records[i].data);
23824             if(n){
23825                 this.tpl.insertBefore(n, d);
23826             }else{
23827                 this.tpl.append(this.el, d);
23828             }
23829         }
23830         this.updateIndexes(index);
23831     },
23832
23833     onRemove : function(ds, record, index){
23834         this.clearSelections();
23835         this.el.dom.removeChild(this.nodes[index]);
23836         this.updateIndexes(index);
23837     },
23838
23839     /**
23840      * Refresh an individual node.
23841      * @param {Number} index
23842      */
23843     refreshNode : function(index){
23844         this.onUpdate(this.store, this.store.getAt(index));
23845     },
23846
23847     updateIndexes : function(startIndex, endIndex){
23848         var ns = this.nodes;
23849         startIndex = startIndex || 0;
23850         endIndex = endIndex || ns.length - 1;
23851         for(var i = startIndex; i <= endIndex; i++){
23852             ns[i].nodeIndex = i;
23853         }
23854     },
23855
23856     /**
23857      * Changes the data store this view uses and refresh the view.
23858      * @param {Store} store
23859      */
23860     setStore : function(store, initial){
23861         if(!initial && this.store){
23862             this.store.un("datachanged", this.refresh);
23863             this.store.un("add", this.onAdd);
23864             this.store.un("remove", this.onRemove);
23865             this.store.un("update", this.onUpdate);
23866             this.store.un("clear", this.refresh);
23867         }
23868         if(store){
23869           
23870             store.on("datachanged", this.refresh, this);
23871             store.on("add", this.onAdd, this);
23872             store.on("remove", this.onRemove, this);
23873             store.on("update", this.onUpdate, this);
23874             store.on("clear", this.refresh, this);
23875         }
23876         
23877         if(store){
23878             this.refresh();
23879         }
23880     },
23881
23882     /**
23883      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
23884      * @param {HTMLElement} node
23885      * @return {HTMLElement} The template node
23886      */
23887     findItemFromChild : function(node){
23888         var el = this.el.dom;
23889         if(!node || node.parentNode == el){
23890                     return node;
23891             }
23892             var p = node.parentNode;
23893             while(p && p != el){
23894             if(p.parentNode == el){
23895                 return p;
23896             }
23897             p = p.parentNode;
23898         }
23899             return null;
23900     },
23901
23902     /** @ignore */
23903     onClick : function(e){
23904         var item = this.findItemFromChild(e.getTarget());
23905         if(item){
23906             var index = this.indexOf(item);
23907             if(this.onItemClick(item, index, e) !== false){
23908                 this.fireEvent("click", this, index, item, e);
23909             }
23910         }else{
23911             this.clearSelections();
23912         }
23913     },
23914
23915     /** @ignore */
23916     onContextMenu : function(e){
23917         var item = this.findItemFromChild(e.getTarget());
23918         if(item){
23919             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
23920         }
23921     },
23922
23923     /** @ignore */
23924     onDblClick : function(e){
23925         var item = this.findItemFromChild(e.getTarget());
23926         if(item){
23927             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
23928         }
23929     },
23930
23931     onItemClick : function(item, index, e)
23932     {
23933         if(this.fireEvent("beforeclick", this, index, item, e) === false){
23934             return false;
23935         }
23936         if (this.toggleSelect) {
23937             var m = this.isSelected(item) ? 'unselect' : 'select';
23938             Roo.log(m);
23939             var _t = this;
23940             _t[m](item, true, false);
23941             return true;
23942         }
23943         if(this.multiSelect || this.singleSelect){
23944             if(this.multiSelect && e.shiftKey && this.lastSelection){
23945                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
23946             }else{
23947                 this.select(item, this.multiSelect && e.ctrlKey);
23948                 this.lastSelection = item;
23949             }
23950             e.preventDefault();
23951         }
23952         return true;
23953     },
23954
23955     /**
23956      * Get the number of selected nodes.
23957      * @return {Number}
23958      */
23959     getSelectionCount : function(){
23960         return this.selections.length;
23961     },
23962
23963     /**
23964      * Get the currently selected nodes.
23965      * @return {Array} An array of HTMLElements
23966      */
23967     getSelectedNodes : function(){
23968         return this.selections;
23969     },
23970
23971     /**
23972      * Get the indexes of the selected nodes.
23973      * @return {Array}
23974      */
23975     getSelectedIndexes : function(){
23976         var indexes = [], s = this.selections;
23977         for(var i = 0, len = s.length; i < len; i++){
23978             indexes.push(s[i].nodeIndex);
23979         }
23980         return indexes;
23981     },
23982
23983     /**
23984      * Clear all selections
23985      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
23986      */
23987     clearSelections : function(suppressEvent){
23988         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
23989             this.cmp.elements = this.selections;
23990             this.cmp.removeClass(this.selectedClass);
23991             this.selections = [];
23992             if(!suppressEvent){
23993                 this.fireEvent("selectionchange", this, this.selections);
23994             }
23995         }
23996     },
23997
23998     /**
23999      * Returns true if the passed node is selected
24000      * @param {HTMLElement/Number} node The node or node index
24001      * @return {Boolean}
24002      */
24003     isSelected : function(node){
24004         var s = this.selections;
24005         if(s.length < 1){
24006             return false;
24007         }
24008         node = this.getNode(node);
24009         return s.indexOf(node) !== -1;
24010     },
24011
24012     /**
24013      * Selects nodes.
24014      * @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
24015      * @param {Boolean} keepExisting (optional) true to keep existing selections
24016      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
24017      */
24018     select : function(nodeInfo, keepExisting, suppressEvent){
24019         if(nodeInfo instanceof Array){
24020             if(!keepExisting){
24021                 this.clearSelections(true);
24022             }
24023             for(var i = 0, len = nodeInfo.length; i < len; i++){
24024                 this.select(nodeInfo[i], true, true);
24025             }
24026             return;
24027         } 
24028         var node = this.getNode(nodeInfo);
24029         if(!node || this.isSelected(node)){
24030             return; // already selected.
24031         }
24032         if(!keepExisting){
24033             this.clearSelections(true);
24034         }
24035         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
24036             Roo.fly(node).addClass(this.selectedClass);
24037             this.selections.push(node);
24038             if(!suppressEvent){
24039                 this.fireEvent("selectionchange", this, this.selections);
24040             }
24041         }
24042         
24043         
24044     },
24045       /**
24046      * Unselects nodes.
24047      * @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
24048      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
24049      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
24050      */
24051     unselect : function(nodeInfo, keepExisting, suppressEvent)
24052     {
24053         if(nodeInfo instanceof Array){
24054             Roo.each(this.selections, function(s) {
24055                 this.unselect(s, nodeInfo);
24056             }, this);
24057             return;
24058         }
24059         var node = this.getNode(nodeInfo);
24060         if(!node || !this.isSelected(node)){
24061             Roo.log("not selected");
24062             return; // not selected.
24063         }
24064         // fireevent???
24065         var ns = [];
24066         Roo.each(this.selections, function(s) {
24067             if (s == node ) {
24068                 Roo.fly(node).removeClass(this.selectedClass);
24069
24070                 return;
24071             }
24072             ns.push(s);
24073         },this);
24074         
24075         this.selections= ns;
24076         this.fireEvent("selectionchange", this, this.selections);
24077     },
24078
24079     /**
24080      * Gets a template node.
24081      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
24082      * @return {HTMLElement} The node or null if it wasn't found
24083      */
24084     getNode : function(nodeInfo){
24085         if(typeof nodeInfo == "string"){
24086             return document.getElementById(nodeInfo);
24087         }else if(typeof nodeInfo == "number"){
24088             return this.nodes[nodeInfo];
24089         }
24090         return nodeInfo;
24091     },
24092
24093     /**
24094      * Gets a range template nodes.
24095      * @param {Number} startIndex
24096      * @param {Number} endIndex
24097      * @return {Array} An array of nodes
24098      */
24099     getNodes : function(start, end){
24100         var ns = this.nodes;
24101         start = start || 0;
24102         end = typeof end == "undefined" ? ns.length - 1 : end;
24103         var nodes = [];
24104         if(start <= end){
24105             for(var i = start; i <= end; i++){
24106                 nodes.push(ns[i]);
24107             }
24108         } else{
24109             for(var i = start; i >= end; i--){
24110                 nodes.push(ns[i]);
24111             }
24112         }
24113         return nodes;
24114     },
24115
24116     /**
24117      * Finds the index of the passed node
24118      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
24119      * @return {Number} The index of the node or -1
24120      */
24121     indexOf : function(node){
24122         node = this.getNode(node);
24123         if(typeof node.nodeIndex == "number"){
24124             return node.nodeIndex;
24125         }
24126         var ns = this.nodes;
24127         for(var i = 0, len = ns.length; i < len; i++){
24128             if(ns[i] == node){
24129                 return i;
24130             }
24131         }
24132         return -1;
24133     }
24134 });
24135 /*
24136  * Based on:
24137  * Ext JS Library 1.1.1
24138  * Copyright(c) 2006-2007, Ext JS, LLC.
24139  *
24140  * Originally Released Under LGPL - original licence link has changed is not relivant.
24141  *
24142  * Fork - LGPL
24143  * <script type="text/javascript">
24144  */
24145
24146 /**
24147  * @class Roo.JsonView
24148  * @extends Roo.View
24149  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
24150 <pre><code>
24151 var view = new Roo.JsonView({
24152     container: "my-element",
24153     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
24154     multiSelect: true, 
24155     jsonRoot: "data" 
24156 });
24157
24158 // listen for node click?
24159 view.on("click", function(vw, index, node, e){
24160     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
24161 });
24162
24163 // direct load of JSON data
24164 view.load("foobar.php");
24165
24166 // Example from my blog list
24167 var tpl = new Roo.Template(
24168     '&lt;div class="entry"&gt;' +
24169     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
24170     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
24171     "&lt;/div&gt;&lt;hr /&gt;"
24172 );
24173
24174 var moreView = new Roo.JsonView({
24175     container :  "entry-list", 
24176     template : tpl,
24177     jsonRoot: "posts"
24178 });
24179 moreView.on("beforerender", this.sortEntries, this);
24180 moreView.load({
24181     url: "/blog/get-posts.php",
24182     params: "allposts=true",
24183     text: "Loading Blog Entries..."
24184 });
24185 </code></pre>
24186
24187 * Note: old code is supported with arguments : (container, template, config)
24188
24189
24190  * @constructor
24191  * Create a new JsonView
24192  * 
24193  * @param {Object} config The config object
24194  * 
24195  */
24196 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
24197     
24198     
24199     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
24200
24201     var um = this.el.getUpdateManager();
24202     um.setRenderer(this);
24203     um.on("update", this.onLoad, this);
24204     um.on("failure", this.onLoadException, this);
24205
24206     /**
24207      * @event beforerender
24208      * Fires before rendering of the downloaded JSON data.
24209      * @param {Roo.JsonView} this
24210      * @param {Object} data The JSON data loaded
24211      */
24212     /**
24213      * @event load
24214      * Fires when data is loaded.
24215      * @param {Roo.JsonView} this
24216      * @param {Object} data The JSON data loaded
24217      * @param {Object} response The raw Connect response object
24218      */
24219     /**
24220      * @event loadexception
24221      * Fires when loading fails.
24222      * @param {Roo.JsonView} this
24223      * @param {Object} response The raw Connect response object
24224      */
24225     this.addEvents({
24226         'beforerender' : true,
24227         'load' : true,
24228         'loadexception' : true
24229     });
24230 };
24231 Roo.extend(Roo.JsonView, Roo.View, {
24232     /**
24233      * @type {String} The root property in the loaded JSON object that contains the data
24234      */
24235     jsonRoot : "",
24236
24237     /**
24238      * Refreshes the view.
24239      */
24240     refresh : function(){
24241         this.clearSelections();
24242         this.el.update("");
24243         var html = [];
24244         var o = this.jsonData;
24245         if(o && o.length > 0){
24246             for(var i = 0, len = o.length; i < len; i++){
24247                 var data = this.prepareData(o[i], i, o);
24248                 html[html.length] = this.tpl.apply(data);
24249             }
24250         }else{
24251             html.push(this.emptyText);
24252         }
24253         this.el.update(html.join(""));
24254         this.nodes = this.el.dom.childNodes;
24255         this.updateIndexes(0);
24256     },
24257
24258     /**
24259      * 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.
24260      * @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:
24261      <pre><code>
24262      view.load({
24263          url: "your-url.php",
24264          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
24265          callback: yourFunction,
24266          scope: yourObject, //(optional scope)
24267          discardUrl: false,
24268          nocache: false,
24269          text: "Loading...",
24270          timeout: 30,
24271          scripts: false
24272      });
24273      </code></pre>
24274      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
24275      * 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.
24276      * @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}
24277      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
24278      * @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.
24279      */
24280     load : function(){
24281         var um = this.el.getUpdateManager();
24282         um.update.apply(um, arguments);
24283     },
24284
24285     render : function(el, response){
24286         this.clearSelections();
24287         this.el.update("");
24288         var o;
24289         try{
24290             o = Roo.util.JSON.decode(response.responseText);
24291             if(this.jsonRoot){
24292                 
24293                 o = o[this.jsonRoot];
24294             }
24295         } catch(e){
24296         }
24297         /**
24298          * The current JSON data or null
24299          */
24300         this.jsonData = o;
24301         this.beforeRender();
24302         this.refresh();
24303     },
24304
24305 /**
24306  * Get the number of records in the current JSON dataset
24307  * @return {Number}
24308  */
24309     getCount : function(){
24310         return this.jsonData ? this.jsonData.length : 0;
24311     },
24312
24313 /**
24314  * Returns the JSON object for the specified node(s)
24315  * @param {HTMLElement/Array} node The node or an array of nodes
24316  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
24317  * you get the JSON object for the node
24318  */
24319     getNodeData : function(node){
24320         if(node instanceof Array){
24321             var data = [];
24322             for(var i = 0, len = node.length; i < len; i++){
24323                 data.push(this.getNodeData(node[i]));
24324             }
24325             return data;
24326         }
24327         return this.jsonData[this.indexOf(node)] || null;
24328     },
24329
24330     beforeRender : function(){
24331         this.snapshot = this.jsonData;
24332         if(this.sortInfo){
24333             this.sort.apply(this, this.sortInfo);
24334         }
24335         this.fireEvent("beforerender", this, this.jsonData);
24336     },
24337
24338     onLoad : function(el, o){
24339         this.fireEvent("load", this, this.jsonData, o);
24340     },
24341
24342     onLoadException : function(el, o){
24343         this.fireEvent("loadexception", this, o);
24344     },
24345
24346 /**
24347  * Filter the data by a specific property.
24348  * @param {String} property A property on your JSON objects
24349  * @param {String/RegExp} value Either string that the property values
24350  * should start with, or a RegExp to test against the property
24351  */
24352     filter : function(property, value){
24353         if(this.jsonData){
24354             var data = [];
24355             var ss = this.snapshot;
24356             if(typeof value == "string"){
24357                 var vlen = value.length;
24358                 if(vlen == 0){
24359                     this.clearFilter();
24360                     return;
24361                 }
24362                 value = value.toLowerCase();
24363                 for(var i = 0, len = ss.length; i < len; i++){
24364                     var o = ss[i];
24365                     if(o[property].substr(0, vlen).toLowerCase() == value){
24366                         data.push(o);
24367                     }
24368                 }
24369             } else if(value.exec){ // regex?
24370                 for(var i = 0, len = ss.length; i < len; i++){
24371                     var o = ss[i];
24372                     if(value.test(o[property])){
24373                         data.push(o);
24374                     }
24375                 }
24376             } else{
24377                 return;
24378             }
24379             this.jsonData = data;
24380             this.refresh();
24381         }
24382     },
24383
24384 /**
24385  * Filter by a function. The passed function will be called with each
24386  * object in the current dataset. If the function returns true the value is kept,
24387  * otherwise it is filtered.
24388  * @param {Function} fn
24389  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
24390  */
24391     filterBy : function(fn, scope){
24392         if(this.jsonData){
24393             var data = [];
24394             var ss = this.snapshot;
24395             for(var i = 0, len = ss.length; i < len; i++){
24396                 var o = ss[i];
24397                 if(fn.call(scope || this, o)){
24398                     data.push(o);
24399                 }
24400             }
24401             this.jsonData = data;
24402             this.refresh();
24403         }
24404     },
24405
24406 /**
24407  * Clears the current filter.
24408  */
24409     clearFilter : function(){
24410         if(this.snapshot && this.jsonData != this.snapshot){
24411             this.jsonData = this.snapshot;
24412             this.refresh();
24413         }
24414     },
24415
24416
24417 /**
24418  * Sorts the data for this view and refreshes it.
24419  * @param {String} property A property on your JSON objects to sort on
24420  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
24421  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
24422  */
24423     sort : function(property, dir, sortType){
24424         this.sortInfo = Array.prototype.slice.call(arguments, 0);
24425         if(this.jsonData){
24426             var p = property;
24427             var dsc = dir && dir.toLowerCase() == "desc";
24428             var f = function(o1, o2){
24429                 var v1 = sortType ? sortType(o1[p]) : o1[p];
24430                 var v2 = sortType ? sortType(o2[p]) : o2[p];
24431                 ;
24432                 if(v1 < v2){
24433                     return dsc ? +1 : -1;
24434                 } else if(v1 > v2){
24435                     return dsc ? -1 : +1;
24436                 } else{
24437                     return 0;
24438                 }
24439             };
24440             this.jsonData.sort(f);
24441             this.refresh();
24442             if(this.jsonData != this.snapshot){
24443                 this.snapshot.sort(f);
24444             }
24445         }
24446     }
24447 });/*
24448  * Based on:
24449  * Ext JS Library 1.1.1
24450  * Copyright(c) 2006-2007, Ext JS, LLC.
24451  *
24452  * Originally Released Under LGPL - original licence link has changed is not relivant.
24453  *
24454  * Fork - LGPL
24455  * <script type="text/javascript">
24456  */
24457  
24458
24459 /**
24460  * @class Roo.ColorPalette
24461  * @extends Roo.Component
24462  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
24463  * Here's an example of typical usage:
24464  * <pre><code>
24465 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
24466 cp.render('my-div');
24467
24468 cp.on('select', function(palette, selColor){
24469     // do something with selColor
24470 });
24471 </code></pre>
24472  * @constructor
24473  * Create a new ColorPalette
24474  * @param {Object} config The config object
24475  */
24476 Roo.ColorPalette = function(config){
24477     Roo.ColorPalette.superclass.constructor.call(this, config);
24478     this.addEvents({
24479         /**
24480              * @event select
24481              * Fires when a color is selected
24482              * @param {ColorPalette} this
24483              * @param {String} color The 6-digit color hex code (without the # symbol)
24484              */
24485         select: true
24486     });
24487
24488     if(this.handler){
24489         this.on("select", this.handler, this.scope, true);
24490     }
24491 };
24492 Roo.extend(Roo.ColorPalette, Roo.Component, {
24493     /**
24494      * @cfg {String} itemCls
24495      * The CSS class to apply to the containing element (defaults to "x-color-palette")
24496      */
24497     itemCls : "x-color-palette",
24498     /**
24499      * @cfg {String} value
24500      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
24501      * the hex codes are case-sensitive.
24502      */
24503     value : null,
24504     clickEvent:'click',
24505     // private
24506     ctype: "Roo.ColorPalette",
24507
24508     /**
24509      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
24510      */
24511     allowReselect : false,
24512
24513     /**
24514      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
24515      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
24516      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
24517      * of colors with the width setting until the box is symmetrical.</p>
24518      * <p>You can override individual colors if needed:</p>
24519      * <pre><code>
24520 var cp = new Roo.ColorPalette();
24521 cp.colors[0] = "FF0000";  // change the first box to red
24522 </code></pre>
24523
24524 Or you can provide a custom array of your own for complete control:
24525 <pre><code>
24526 var cp = new Roo.ColorPalette();
24527 cp.colors = ["000000", "993300", "333300"];
24528 </code></pre>
24529      * @type Array
24530      */
24531     colors : [
24532         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
24533         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
24534         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
24535         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
24536         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
24537     ],
24538
24539     // private
24540     onRender : function(container, position){
24541         var t = new Roo.MasterTemplate(
24542             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
24543         );
24544         var c = this.colors;
24545         for(var i = 0, len = c.length; i < len; i++){
24546             t.add([c[i]]);
24547         }
24548         var el = document.createElement("div");
24549         el.className = this.itemCls;
24550         t.overwrite(el);
24551         container.dom.insertBefore(el, position);
24552         this.el = Roo.get(el);
24553         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
24554         if(this.clickEvent != 'click'){
24555             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
24556         }
24557     },
24558
24559     // private
24560     afterRender : function(){
24561         Roo.ColorPalette.superclass.afterRender.call(this);
24562         if(this.value){
24563             var s = this.value;
24564             this.value = null;
24565             this.select(s);
24566         }
24567     },
24568
24569     // private
24570     handleClick : function(e, t){
24571         e.preventDefault();
24572         if(!this.disabled){
24573             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
24574             this.select(c.toUpperCase());
24575         }
24576     },
24577
24578     /**
24579      * Selects the specified color in the palette (fires the select event)
24580      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
24581      */
24582     select : function(color){
24583         color = color.replace("#", "");
24584         if(color != this.value || this.allowReselect){
24585             var el = this.el;
24586             if(this.value){
24587                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
24588             }
24589             el.child("a.color-"+color).addClass("x-color-palette-sel");
24590             this.value = color;
24591             this.fireEvent("select", this, color);
24592         }
24593     }
24594 });/*
24595  * Based on:
24596  * Ext JS Library 1.1.1
24597  * Copyright(c) 2006-2007, Ext JS, LLC.
24598  *
24599  * Originally Released Under LGPL - original licence link has changed is not relivant.
24600  *
24601  * Fork - LGPL
24602  * <script type="text/javascript">
24603  */
24604  
24605 /**
24606  * @class Roo.DatePicker
24607  * @extends Roo.Component
24608  * Simple date picker class.
24609  * @constructor
24610  * Create a new DatePicker
24611  * @param {Object} config The config object
24612  */
24613 Roo.DatePicker = function(config){
24614     Roo.DatePicker.superclass.constructor.call(this, config);
24615
24616     this.value = config && config.value ?
24617                  config.value.clearTime() : new Date().clearTime();
24618
24619     this.addEvents({
24620         /**
24621              * @event select
24622              * Fires when a date is selected
24623              * @param {DatePicker} this
24624              * @param {Date} date The selected date
24625              */
24626         'select': true,
24627         /**
24628              * @event monthchange
24629              * Fires when the displayed month changes 
24630              * @param {DatePicker} this
24631              * @param {Date} date The selected month
24632              */
24633         'monthchange': true
24634     });
24635
24636     if(this.handler){
24637         this.on("select", this.handler,  this.scope || this);
24638     }
24639     // build the disabledDatesRE
24640     if(!this.disabledDatesRE && this.disabledDates){
24641         var dd = this.disabledDates;
24642         var re = "(?:";
24643         for(var i = 0; i < dd.length; i++){
24644             re += dd[i];
24645             if(i != dd.length-1) re += "|";
24646         }
24647         this.disabledDatesRE = new RegExp(re + ")");
24648     }
24649 };
24650
24651 Roo.extend(Roo.DatePicker, Roo.Component, {
24652     /**
24653      * @cfg {String} todayText
24654      * The text to display on the button that selects the current date (defaults to "Today")
24655      */
24656     todayText : "Today",
24657     /**
24658      * @cfg {String} okText
24659      * The text to display on the ok button
24660      */
24661     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
24662     /**
24663      * @cfg {String} cancelText
24664      * The text to display on the cancel button
24665      */
24666     cancelText : "Cancel",
24667     /**
24668      * @cfg {String} todayTip
24669      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
24670      */
24671     todayTip : "{0} (Spacebar)",
24672     /**
24673      * @cfg {Date} minDate
24674      * Minimum allowable date (JavaScript date object, defaults to null)
24675      */
24676     minDate : null,
24677     /**
24678      * @cfg {Date} maxDate
24679      * Maximum allowable date (JavaScript date object, defaults to null)
24680      */
24681     maxDate : null,
24682     /**
24683      * @cfg {String} minText
24684      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
24685      */
24686     minText : "This date is before the minimum date",
24687     /**
24688      * @cfg {String} maxText
24689      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
24690      */
24691     maxText : "This date is after the maximum date",
24692     /**
24693      * @cfg {String} format
24694      * The default date format string which can be overriden for localization support.  The format must be
24695      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
24696      */
24697     format : "m/d/y",
24698     /**
24699      * @cfg {Array} disabledDays
24700      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
24701      */
24702     disabledDays : null,
24703     /**
24704      * @cfg {String} disabledDaysText
24705      * The tooltip to display when the date falls on a disabled day (defaults to "")
24706      */
24707     disabledDaysText : "",
24708     /**
24709      * @cfg {RegExp} disabledDatesRE
24710      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
24711      */
24712     disabledDatesRE : null,
24713     /**
24714      * @cfg {String} disabledDatesText
24715      * The tooltip text to display when the date falls on a disabled date (defaults to "")
24716      */
24717     disabledDatesText : "",
24718     /**
24719      * @cfg {Boolean} constrainToViewport
24720      * True to constrain the date picker to the viewport (defaults to true)
24721      */
24722     constrainToViewport : true,
24723     /**
24724      * @cfg {Array} monthNames
24725      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
24726      */
24727     monthNames : Date.monthNames,
24728     /**
24729      * @cfg {Array} dayNames
24730      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
24731      */
24732     dayNames : Date.dayNames,
24733     /**
24734      * @cfg {String} nextText
24735      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
24736      */
24737     nextText: 'Next Month (Control+Right)',
24738     /**
24739      * @cfg {String} prevText
24740      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
24741      */
24742     prevText: 'Previous Month (Control+Left)',
24743     /**
24744      * @cfg {String} monthYearText
24745      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
24746      */
24747     monthYearText: 'Choose a month (Control+Up/Down to move years)',
24748     /**
24749      * @cfg {Number} startDay
24750      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
24751      */
24752     startDay : 0,
24753     /**
24754      * @cfg {Bool} showClear
24755      * Show a clear button (usefull for date form elements that can be blank.)
24756      */
24757     
24758     showClear: false,
24759     
24760     /**
24761      * Sets the value of the date field
24762      * @param {Date} value The date to set
24763      */
24764     setValue : function(value){
24765         var old = this.value;
24766         this.value = value.clearTime(true);
24767         if(this.el){
24768             this.update(this.value);
24769         }
24770     },
24771
24772     /**
24773      * Gets the current selected value of the date field
24774      * @return {Date} The selected date
24775      */
24776     getValue : function(){
24777         return this.value;
24778     },
24779
24780     // private
24781     focus : function(){
24782         if(this.el){
24783             this.update(this.activeDate);
24784         }
24785     },
24786
24787     // private
24788     onRender : function(container, position){
24789         var m = [
24790              '<table cellspacing="0">',
24791                 '<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>',
24792                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
24793         var dn = this.dayNames;
24794         for(var i = 0; i < 7; i++){
24795             var d = this.startDay+i;
24796             if(d > 6){
24797                 d = d-7;
24798             }
24799             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
24800         }
24801         m[m.length] = "</tr></thead><tbody><tr>";
24802         for(var i = 0; i < 42; i++) {
24803             if(i % 7 == 0 && i != 0){
24804                 m[m.length] = "</tr><tr>";
24805             }
24806             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
24807         }
24808         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
24809             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
24810
24811         var el = document.createElement("div");
24812         el.className = "x-date-picker";
24813         el.innerHTML = m.join("");
24814
24815         container.dom.insertBefore(el, position);
24816
24817         this.el = Roo.get(el);
24818         this.eventEl = Roo.get(el.firstChild);
24819
24820         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
24821             handler: this.showPrevMonth,
24822             scope: this,
24823             preventDefault:true,
24824             stopDefault:true
24825         });
24826
24827         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
24828             handler: this.showNextMonth,
24829             scope: this,
24830             preventDefault:true,
24831             stopDefault:true
24832         });
24833
24834         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
24835
24836         this.monthPicker = this.el.down('div.x-date-mp');
24837         this.monthPicker.enableDisplayMode('block');
24838         
24839         var kn = new Roo.KeyNav(this.eventEl, {
24840             "left" : function(e){
24841                 e.ctrlKey ?
24842                     this.showPrevMonth() :
24843                     this.update(this.activeDate.add("d", -1));
24844             },
24845
24846             "right" : function(e){
24847                 e.ctrlKey ?
24848                     this.showNextMonth() :
24849                     this.update(this.activeDate.add("d", 1));
24850             },
24851
24852             "up" : function(e){
24853                 e.ctrlKey ?
24854                     this.showNextYear() :
24855                     this.update(this.activeDate.add("d", -7));
24856             },
24857
24858             "down" : function(e){
24859                 e.ctrlKey ?
24860                     this.showPrevYear() :
24861                     this.update(this.activeDate.add("d", 7));
24862             },
24863
24864             "pageUp" : function(e){
24865                 this.showNextMonth();
24866             },
24867
24868             "pageDown" : function(e){
24869                 this.showPrevMonth();
24870             },
24871
24872             "enter" : function(e){
24873                 e.stopPropagation();
24874                 return true;
24875             },
24876
24877             scope : this
24878         });
24879
24880         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
24881
24882         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
24883
24884         this.el.unselectable();
24885         
24886         this.cells = this.el.select("table.x-date-inner tbody td");
24887         this.textNodes = this.el.query("table.x-date-inner tbody span");
24888
24889         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
24890             text: "&#160;",
24891             tooltip: this.monthYearText
24892         });
24893
24894         this.mbtn.on('click', this.showMonthPicker, this);
24895         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
24896
24897
24898         var today = (new Date()).dateFormat(this.format);
24899         
24900         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
24901         if (this.showClear) {
24902             baseTb.add( new Roo.Toolbar.Fill());
24903         }
24904         baseTb.add({
24905             text: String.format(this.todayText, today),
24906             tooltip: String.format(this.todayTip, today),
24907             handler: this.selectToday,
24908             scope: this
24909         });
24910         
24911         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
24912             
24913         //});
24914         if (this.showClear) {
24915             
24916             baseTb.add( new Roo.Toolbar.Fill());
24917             baseTb.add({
24918                 text: '&#160;',
24919                 cls: 'x-btn-icon x-btn-clear',
24920                 handler: function() {
24921                     //this.value = '';
24922                     this.fireEvent("select", this, '');
24923                 },
24924                 scope: this
24925             });
24926         }
24927         
24928         
24929         if(Roo.isIE){
24930             this.el.repaint();
24931         }
24932         this.update(this.value);
24933     },
24934
24935     createMonthPicker : function(){
24936         if(!this.monthPicker.dom.firstChild){
24937             var buf = ['<table border="0" cellspacing="0">'];
24938             for(var i = 0; i < 6; i++){
24939                 buf.push(
24940                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
24941                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
24942                     i == 0 ?
24943                     '<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>' :
24944                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
24945                 );
24946             }
24947             buf.push(
24948                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
24949                     this.okText,
24950                     '</button><button type="button" class="x-date-mp-cancel">',
24951                     this.cancelText,
24952                     '</button></td></tr>',
24953                 '</table>'
24954             );
24955             this.monthPicker.update(buf.join(''));
24956             this.monthPicker.on('click', this.onMonthClick, this);
24957             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
24958
24959             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
24960             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
24961
24962             this.mpMonths.each(function(m, a, i){
24963                 i += 1;
24964                 if((i%2) == 0){
24965                     m.dom.xmonth = 5 + Math.round(i * .5);
24966                 }else{
24967                     m.dom.xmonth = Math.round((i-1) * .5);
24968                 }
24969             });
24970         }
24971     },
24972
24973     showMonthPicker : function(){
24974         this.createMonthPicker();
24975         var size = this.el.getSize();
24976         this.monthPicker.setSize(size);
24977         this.monthPicker.child('table').setSize(size);
24978
24979         this.mpSelMonth = (this.activeDate || this.value).getMonth();
24980         this.updateMPMonth(this.mpSelMonth);
24981         this.mpSelYear = (this.activeDate || this.value).getFullYear();
24982         this.updateMPYear(this.mpSelYear);
24983
24984         this.monthPicker.slideIn('t', {duration:.2});
24985     },
24986
24987     updateMPYear : function(y){
24988         this.mpyear = y;
24989         var ys = this.mpYears.elements;
24990         for(var i = 1; i <= 10; i++){
24991             var td = ys[i-1], y2;
24992             if((i%2) == 0){
24993                 y2 = y + Math.round(i * .5);
24994                 td.firstChild.innerHTML = y2;
24995                 td.xyear = y2;
24996             }else{
24997                 y2 = y - (5-Math.round(i * .5));
24998                 td.firstChild.innerHTML = y2;
24999                 td.xyear = y2;
25000             }
25001             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
25002         }
25003     },
25004
25005     updateMPMonth : function(sm){
25006         this.mpMonths.each(function(m, a, i){
25007             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
25008         });
25009     },
25010
25011     selectMPMonth: function(m){
25012         
25013     },
25014
25015     onMonthClick : function(e, t){
25016         e.stopEvent();
25017         var el = new Roo.Element(t), pn;
25018         if(el.is('button.x-date-mp-cancel')){
25019             this.hideMonthPicker();
25020         }
25021         else if(el.is('button.x-date-mp-ok')){
25022             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
25023             this.hideMonthPicker();
25024         }
25025         else if(pn = el.up('td.x-date-mp-month', 2)){
25026             this.mpMonths.removeClass('x-date-mp-sel');
25027             pn.addClass('x-date-mp-sel');
25028             this.mpSelMonth = pn.dom.xmonth;
25029         }
25030         else if(pn = el.up('td.x-date-mp-year', 2)){
25031             this.mpYears.removeClass('x-date-mp-sel');
25032             pn.addClass('x-date-mp-sel');
25033             this.mpSelYear = pn.dom.xyear;
25034         }
25035         else if(el.is('a.x-date-mp-prev')){
25036             this.updateMPYear(this.mpyear-10);
25037         }
25038         else if(el.is('a.x-date-mp-next')){
25039             this.updateMPYear(this.mpyear+10);
25040         }
25041     },
25042
25043     onMonthDblClick : function(e, t){
25044         e.stopEvent();
25045         var el = new Roo.Element(t), pn;
25046         if(pn = el.up('td.x-date-mp-month', 2)){
25047             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
25048             this.hideMonthPicker();
25049         }
25050         else if(pn = el.up('td.x-date-mp-year', 2)){
25051             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
25052             this.hideMonthPicker();
25053         }
25054     },
25055
25056     hideMonthPicker : function(disableAnim){
25057         if(this.monthPicker){
25058             if(disableAnim === true){
25059                 this.monthPicker.hide();
25060             }else{
25061                 this.monthPicker.slideOut('t', {duration:.2});
25062             }
25063         }
25064     },
25065
25066     // private
25067     showPrevMonth : function(e){
25068         this.update(this.activeDate.add("mo", -1));
25069     },
25070
25071     // private
25072     showNextMonth : function(e){
25073         this.update(this.activeDate.add("mo", 1));
25074     },
25075
25076     // private
25077     showPrevYear : function(){
25078         this.update(this.activeDate.add("y", -1));
25079     },
25080
25081     // private
25082     showNextYear : function(){
25083         this.update(this.activeDate.add("y", 1));
25084     },
25085
25086     // private
25087     handleMouseWheel : function(e){
25088         var delta = e.getWheelDelta();
25089         if(delta > 0){
25090             this.showPrevMonth();
25091             e.stopEvent();
25092         } else if(delta < 0){
25093             this.showNextMonth();
25094             e.stopEvent();
25095         }
25096     },
25097
25098     // private
25099     handleDateClick : function(e, t){
25100         e.stopEvent();
25101         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
25102             this.setValue(new Date(t.dateValue));
25103             this.fireEvent("select", this, this.value);
25104         }
25105     },
25106
25107     // private
25108     selectToday : function(){
25109         this.setValue(new Date().clearTime());
25110         this.fireEvent("select", this, this.value);
25111     },
25112
25113     // private
25114     update : function(date)
25115     {
25116         var vd = this.activeDate;
25117         this.activeDate = date;
25118         if(vd && this.el){
25119             var t = date.getTime();
25120             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
25121                 this.cells.removeClass("x-date-selected");
25122                 this.cells.each(function(c){
25123                    if(c.dom.firstChild.dateValue == t){
25124                        c.addClass("x-date-selected");
25125                        setTimeout(function(){
25126                             try{c.dom.firstChild.focus();}catch(e){}
25127                        }, 50);
25128                        return false;
25129                    }
25130                 });
25131                 return;
25132             }
25133         }
25134         
25135         var days = date.getDaysInMonth();
25136         var firstOfMonth = date.getFirstDateOfMonth();
25137         var startingPos = firstOfMonth.getDay()-this.startDay;
25138
25139         if(startingPos <= this.startDay){
25140             startingPos += 7;
25141         }
25142
25143         var pm = date.add("mo", -1);
25144         var prevStart = pm.getDaysInMonth()-startingPos;
25145
25146         var cells = this.cells.elements;
25147         var textEls = this.textNodes;
25148         days += startingPos;
25149
25150         // convert everything to numbers so it's fast
25151         var day = 86400000;
25152         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
25153         var today = new Date().clearTime().getTime();
25154         var sel = date.clearTime().getTime();
25155         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
25156         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
25157         var ddMatch = this.disabledDatesRE;
25158         var ddText = this.disabledDatesText;
25159         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
25160         var ddaysText = this.disabledDaysText;
25161         var format = this.format;
25162
25163         var setCellClass = function(cal, cell){
25164             cell.title = "";
25165             var t = d.getTime();
25166             cell.firstChild.dateValue = t;
25167             if(t == today){
25168                 cell.className += " x-date-today";
25169                 cell.title = cal.todayText;
25170             }
25171             if(t == sel){
25172                 cell.className += " x-date-selected";
25173                 setTimeout(function(){
25174                     try{cell.firstChild.focus();}catch(e){}
25175                 }, 50);
25176             }
25177             // disabling
25178             if(t < min) {
25179                 cell.className = " x-date-disabled";
25180                 cell.title = cal.minText;
25181                 return;
25182             }
25183             if(t > max) {
25184                 cell.className = " x-date-disabled";
25185                 cell.title = cal.maxText;
25186                 return;
25187             }
25188             if(ddays){
25189                 if(ddays.indexOf(d.getDay()) != -1){
25190                     cell.title = ddaysText;
25191                     cell.className = " x-date-disabled";
25192                 }
25193             }
25194             if(ddMatch && format){
25195                 var fvalue = d.dateFormat(format);
25196                 if(ddMatch.test(fvalue)){
25197                     cell.title = ddText.replace("%0", fvalue);
25198                     cell.className = " x-date-disabled";
25199                 }
25200             }
25201         };
25202
25203         var i = 0;
25204         for(; i < startingPos; i++) {
25205             textEls[i].innerHTML = (++prevStart);
25206             d.setDate(d.getDate()+1);
25207             cells[i].className = "x-date-prevday";
25208             setCellClass(this, cells[i]);
25209         }
25210         for(; i < days; i++){
25211             intDay = i - startingPos + 1;
25212             textEls[i].innerHTML = (intDay);
25213             d.setDate(d.getDate()+1);
25214             cells[i].className = "x-date-active";
25215             setCellClass(this, cells[i]);
25216         }
25217         var extraDays = 0;
25218         for(; i < 42; i++) {
25219              textEls[i].innerHTML = (++extraDays);
25220              d.setDate(d.getDate()+1);
25221              cells[i].className = "x-date-nextday";
25222              setCellClass(this, cells[i]);
25223         }
25224
25225         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
25226         this.fireEvent('monthchange', this, date);
25227         
25228         if(!this.internalRender){
25229             var main = this.el.dom.firstChild;
25230             var w = main.offsetWidth;
25231             this.el.setWidth(w + this.el.getBorderWidth("lr"));
25232             Roo.fly(main).setWidth(w);
25233             this.internalRender = true;
25234             // opera does not respect the auto grow header center column
25235             // then, after it gets a width opera refuses to recalculate
25236             // without a second pass
25237             if(Roo.isOpera && !this.secondPass){
25238                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
25239                 this.secondPass = true;
25240                 this.update.defer(10, this, [date]);
25241             }
25242         }
25243         
25244         
25245     }
25246 });        /*
25247  * Based on:
25248  * Ext JS Library 1.1.1
25249  * Copyright(c) 2006-2007, Ext JS, LLC.
25250  *
25251  * Originally Released Under LGPL - original licence link has changed is not relivant.
25252  *
25253  * Fork - LGPL
25254  * <script type="text/javascript">
25255  */
25256 /**
25257  * @class Roo.TabPanel
25258  * @extends Roo.util.Observable
25259  * A lightweight tab container.
25260  * <br><br>
25261  * Usage:
25262  * <pre><code>
25263 // basic tabs 1, built from existing content
25264 var tabs = new Roo.TabPanel("tabs1");
25265 tabs.addTab("script", "View Script");
25266 tabs.addTab("markup", "View Markup");
25267 tabs.activate("script");
25268
25269 // more advanced tabs, built from javascript
25270 var jtabs = new Roo.TabPanel("jtabs");
25271 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
25272
25273 // set up the UpdateManager
25274 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
25275 var updater = tab2.getUpdateManager();
25276 updater.setDefaultUrl("ajax1.htm");
25277 tab2.on('activate', updater.refresh, updater, true);
25278
25279 // Use setUrl for Ajax loading
25280 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
25281 tab3.setUrl("ajax2.htm", null, true);
25282
25283 // Disabled tab
25284 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
25285 tab4.disable();
25286
25287 jtabs.activate("jtabs-1");
25288  * </code></pre>
25289  * @constructor
25290  * Create a new TabPanel.
25291  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
25292  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
25293  */
25294 Roo.TabPanel = function(container, config){
25295     /**
25296     * The container element for this TabPanel.
25297     * @type Roo.Element
25298     */
25299     this.el = Roo.get(container, true);
25300     if(config){
25301         if(typeof config == "boolean"){
25302             this.tabPosition = config ? "bottom" : "top";
25303         }else{
25304             Roo.apply(this, config);
25305         }
25306     }
25307     if(this.tabPosition == "bottom"){
25308         this.bodyEl = Roo.get(this.createBody(this.el.dom));
25309         this.el.addClass("x-tabs-bottom");
25310     }
25311     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
25312     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
25313     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
25314     if(Roo.isIE){
25315         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
25316     }
25317     if(this.tabPosition != "bottom"){
25318         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
25319          * @type Roo.Element
25320          */
25321         this.bodyEl = Roo.get(this.createBody(this.el.dom));
25322         this.el.addClass("x-tabs-top");
25323     }
25324     this.items = [];
25325
25326     this.bodyEl.setStyle("position", "relative");
25327
25328     this.active = null;
25329     this.activateDelegate = this.activate.createDelegate(this);
25330
25331     this.addEvents({
25332         /**
25333          * @event tabchange
25334          * Fires when the active tab changes
25335          * @param {Roo.TabPanel} this
25336          * @param {Roo.TabPanelItem} activePanel The new active tab
25337          */
25338         "tabchange": true,
25339         /**
25340          * @event beforetabchange
25341          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
25342          * @param {Roo.TabPanel} this
25343          * @param {Object} e Set cancel to true on this object to cancel the tab change
25344          * @param {Roo.TabPanelItem} tab The tab being changed to
25345          */
25346         "beforetabchange" : true
25347     });
25348
25349     Roo.EventManager.onWindowResize(this.onResize, this);
25350     this.cpad = this.el.getPadding("lr");
25351     this.hiddenCount = 0;
25352
25353
25354     // toolbar on the tabbar support...
25355     if (this.toolbar) {
25356         var tcfg = this.toolbar;
25357         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
25358         this.toolbar = new Roo.Toolbar(tcfg);
25359         if (Roo.isSafari) {
25360             var tbl = tcfg.container.child('table', true);
25361             tbl.setAttribute('width', '100%');
25362         }
25363         
25364     }
25365    
25366
25367
25368     Roo.TabPanel.superclass.constructor.call(this);
25369 };
25370
25371 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
25372     /*
25373      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
25374      */
25375     tabPosition : "top",
25376     /*
25377      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
25378      */
25379     currentTabWidth : 0,
25380     /*
25381      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
25382      */
25383     minTabWidth : 40,
25384     /*
25385      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
25386      */
25387     maxTabWidth : 250,
25388     /*
25389      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
25390      */
25391     preferredTabWidth : 175,
25392     /*
25393      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
25394      */
25395     resizeTabs : false,
25396     /*
25397      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
25398      */
25399     monitorResize : true,
25400     /*
25401      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
25402      */
25403     toolbar : false,
25404
25405     /**
25406      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
25407      * @param {String} id The id of the div to use <b>or create</b>
25408      * @param {String} text The text for the tab
25409      * @param {String} content (optional) Content to put in the TabPanelItem body
25410      * @param {Boolean} closable (optional) True to create a close icon on the tab
25411      * @return {Roo.TabPanelItem} The created TabPanelItem
25412      */
25413     addTab : function(id, text, content, closable){
25414         var item = new Roo.TabPanelItem(this, id, text, closable);
25415         this.addTabItem(item);
25416         if(content){
25417             item.setContent(content);
25418         }
25419         return item;
25420     },
25421
25422     /**
25423      * Returns the {@link Roo.TabPanelItem} with the specified id/index
25424      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
25425      * @return {Roo.TabPanelItem}
25426      */
25427     getTab : function(id){
25428         return this.items[id];
25429     },
25430
25431     /**
25432      * Hides the {@link Roo.TabPanelItem} with the specified id/index
25433      * @param {String/Number} id The id or index of the TabPanelItem to hide.
25434      */
25435     hideTab : function(id){
25436         var t = this.items[id];
25437         if(!t.isHidden()){
25438            t.setHidden(true);
25439            this.hiddenCount++;
25440            this.autoSizeTabs();
25441         }
25442     },
25443
25444     /**
25445      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
25446      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
25447      */
25448     unhideTab : function(id){
25449         var t = this.items[id];
25450         if(t.isHidden()){
25451            t.setHidden(false);
25452            this.hiddenCount--;
25453            this.autoSizeTabs();
25454         }
25455     },
25456
25457     /**
25458      * Adds an existing {@link Roo.TabPanelItem}.
25459      * @param {Roo.TabPanelItem} item The TabPanelItem to add
25460      */
25461     addTabItem : function(item){
25462         this.items[item.id] = item;
25463         this.items.push(item);
25464         if(this.resizeTabs){
25465            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
25466            this.autoSizeTabs();
25467         }else{
25468             item.autoSize();
25469         }
25470     },
25471
25472     /**
25473      * Removes a {@link Roo.TabPanelItem}.
25474      * @param {String/Number} id The id or index of the TabPanelItem to remove.
25475      */
25476     removeTab : function(id){
25477         var items = this.items;
25478         var tab = items[id];
25479         if(!tab) { return; }
25480         var index = items.indexOf(tab);
25481         if(this.active == tab && items.length > 1){
25482             var newTab = this.getNextAvailable(index);
25483             if(newTab) {
25484                 newTab.activate();
25485             }
25486         }
25487         this.stripEl.dom.removeChild(tab.pnode.dom);
25488         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
25489             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
25490         }
25491         items.splice(index, 1);
25492         delete this.items[tab.id];
25493         tab.fireEvent("close", tab);
25494         tab.purgeListeners();
25495         this.autoSizeTabs();
25496     },
25497
25498     getNextAvailable : function(start){
25499         var items = this.items;
25500         var index = start;
25501         // look for a next tab that will slide over to
25502         // replace the one being removed
25503         while(index < items.length){
25504             var item = items[++index];
25505             if(item && !item.isHidden()){
25506                 return item;
25507             }
25508         }
25509         // if one isn't found select the previous tab (on the left)
25510         index = start;
25511         while(index >= 0){
25512             var item = items[--index];
25513             if(item && !item.isHidden()){
25514                 return item;
25515             }
25516         }
25517         return null;
25518     },
25519
25520     /**
25521      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
25522      * @param {String/Number} id The id or index of the TabPanelItem to disable.
25523      */
25524     disableTab : function(id){
25525         var tab = this.items[id];
25526         if(tab && this.active != tab){
25527             tab.disable();
25528         }
25529     },
25530
25531     /**
25532      * Enables a {@link Roo.TabPanelItem} that is disabled.
25533      * @param {String/Number} id The id or index of the TabPanelItem to enable.
25534      */
25535     enableTab : function(id){
25536         var tab = this.items[id];
25537         tab.enable();
25538     },
25539
25540     /**
25541      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
25542      * @param {String/Number} id The id or index of the TabPanelItem to activate.
25543      * @return {Roo.TabPanelItem} The TabPanelItem.
25544      */
25545     activate : function(id){
25546         var tab = this.items[id];
25547         if(!tab){
25548             return null;
25549         }
25550         if(tab == this.active || tab.disabled){
25551             return tab;
25552         }
25553         var e = {};
25554         this.fireEvent("beforetabchange", this, e, tab);
25555         if(e.cancel !== true && !tab.disabled){
25556             if(this.active){
25557                 this.active.hide();
25558             }
25559             this.active = this.items[id];
25560             this.active.show();
25561             this.fireEvent("tabchange", this, this.active);
25562         }
25563         return tab;
25564     },
25565
25566     /**
25567      * Gets the active {@link Roo.TabPanelItem}.
25568      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
25569      */
25570     getActiveTab : function(){
25571         return this.active;
25572     },
25573
25574     /**
25575      * Updates the tab body element to fit the height of the container element
25576      * for overflow scrolling
25577      * @param {Number} targetHeight (optional) Override the starting height from the elements height
25578      */
25579     syncHeight : function(targetHeight){
25580         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
25581         var bm = this.bodyEl.getMargins();
25582         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
25583         this.bodyEl.setHeight(newHeight);
25584         return newHeight;
25585     },
25586
25587     onResize : function(){
25588         if(this.monitorResize){
25589             this.autoSizeTabs();
25590         }
25591     },
25592
25593     /**
25594      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
25595      */
25596     beginUpdate : function(){
25597         this.updating = true;
25598     },
25599
25600     /**
25601      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
25602      */
25603     endUpdate : function(){
25604         this.updating = false;
25605         this.autoSizeTabs();
25606     },
25607
25608     /**
25609      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
25610      */
25611     autoSizeTabs : function(){
25612         var count = this.items.length;
25613         var vcount = count - this.hiddenCount;
25614         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
25615         var w = Math.max(this.el.getWidth() - this.cpad, 10);
25616         var availWidth = Math.floor(w / vcount);
25617         var b = this.stripBody;
25618         if(b.getWidth() > w){
25619             var tabs = this.items;
25620             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
25621             if(availWidth < this.minTabWidth){
25622                 /*if(!this.sleft){    // incomplete scrolling code
25623                     this.createScrollButtons();
25624                 }
25625                 this.showScroll();
25626                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
25627             }
25628         }else{
25629             if(this.currentTabWidth < this.preferredTabWidth){
25630                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
25631             }
25632         }
25633     },
25634
25635     /**
25636      * Returns the number of tabs in this TabPanel.
25637      * @return {Number}
25638      */
25639      getCount : function(){
25640          return this.items.length;
25641      },
25642
25643     /**
25644      * Resizes all the tabs to the passed width
25645      * @param {Number} The new width
25646      */
25647     setTabWidth : function(width){
25648         this.currentTabWidth = width;
25649         for(var i = 0, len = this.items.length; i < len; i++) {
25650                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
25651         }
25652     },
25653
25654     /**
25655      * Destroys this TabPanel
25656      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
25657      */
25658     destroy : function(removeEl){
25659         Roo.EventManager.removeResizeListener(this.onResize, this);
25660         for(var i = 0, len = this.items.length; i < len; i++){
25661             this.items[i].purgeListeners();
25662         }
25663         if(removeEl === true){
25664             this.el.update("");
25665             this.el.remove();
25666         }
25667     }
25668 });
25669
25670 /**
25671  * @class Roo.TabPanelItem
25672  * @extends Roo.util.Observable
25673  * Represents an individual item (tab plus body) in a TabPanel.
25674  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
25675  * @param {String} id The id of this TabPanelItem
25676  * @param {String} text The text for the tab of this TabPanelItem
25677  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
25678  */
25679 Roo.TabPanelItem = function(tabPanel, id, text, closable){
25680     /**
25681      * The {@link Roo.TabPanel} this TabPanelItem belongs to
25682      * @type Roo.TabPanel
25683      */
25684     this.tabPanel = tabPanel;
25685     /**
25686      * The id for this TabPanelItem
25687      * @type String
25688      */
25689     this.id = id;
25690     /** @private */
25691     this.disabled = false;
25692     /** @private */
25693     this.text = text;
25694     /** @private */
25695     this.loaded = false;
25696     this.closable = closable;
25697
25698     /**
25699      * The body element for this TabPanelItem.
25700      * @type Roo.Element
25701      */
25702     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
25703     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
25704     this.bodyEl.setStyle("display", "block");
25705     this.bodyEl.setStyle("zoom", "1");
25706     this.hideAction();
25707
25708     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
25709     /** @private */
25710     this.el = Roo.get(els.el, true);
25711     this.inner = Roo.get(els.inner, true);
25712     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
25713     this.pnode = Roo.get(els.el.parentNode, true);
25714     this.el.on("mousedown", this.onTabMouseDown, this);
25715     this.el.on("click", this.onTabClick, this);
25716     /** @private */
25717     if(closable){
25718         var c = Roo.get(els.close, true);
25719         c.dom.title = this.closeText;
25720         c.addClassOnOver("close-over");
25721         c.on("click", this.closeClick, this);
25722      }
25723
25724     this.addEvents({
25725          /**
25726          * @event activate
25727          * Fires when this tab becomes the active tab.
25728          * @param {Roo.TabPanel} tabPanel The parent TabPanel
25729          * @param {Roo.TabPanelItem} this
25730          */
25731         "activate": true,
25732         /**
25733          * @event beforeclose
25734          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
25735          * @param {Roo.TabPanelItem} this
25736          * @param {Object} e Set cancel to true on this object to cancel the close.
25737          */
25738         "beforeclose": true,
25739         /**
25740          * @event close
25741          * Fires when this tab is closed.
25742          * @param {Roo.TabPanelItem} this
25743          */
25744          "close": true,
25745         /**
25746          * @event deactivate
25747          * Fires when this tab is no longer the active tab.
25748          * @param {Roo.TabPanel} tabPanel The parent TabPanel
25749          * @param {Roo.TabPanelItem} this
25750          */
25751          "deactivate" : true
25752     });
25753     this.hidden = false;
25754
25755     Roo.TabPanelItem.superclass.constructor.call(this);
25756 };
25757
25758 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
25759     purgeListeners : function(){
25760        Roo.util.Observable.prototype.purgeListeners.call(this);
25761        this.el.removeAllListeners();
25762     },
25763     /**
25764      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
25765      */
25766     show : function(){
25767         this.pnode.addClass("on");
25768         this.showAction();
25769         if(Roo.isOpera){
25770             this.tabPanel.stripWrap.repaint();
25771         }
25772         this.fireEvent("activate", this.tabPanel, this);
25773     },
25774
25775     /**
25776      * Returns true if this tab is the active tab.
25777      * @return {Boolean}
25778      */
25779     isActive : function(){
25780         return this.tabPanel.getActiveTab() == this;
25781     },
25782
25783     /**
25784      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
25785      */
25786     hide : function(){
25787         this.pnode.removeClass("on");
25788         this.hideAction();
25789         this.fireEvent("deactivate", this.tabPanel, this);
25790     },
25791
25792     hideAction : function(){
25793         this.bodyEl.hide();
25794         this.bodyEl.setStyle("position", "absolute");
25795         this.bodyEl.setLeft("-20000px");
25796         this.bodyEl.setTop("-20000px");
25797     },
25798
25799     showAction : function(){
25800         this.bodyEl.setStyle("position", "relative");
25801         this.bodyEl.setTop("");
25802         this.bodyEl.setLeft("");
25803         this.bodyEl.show();
25804     },
25805
25806     /**
25807      * Set the tooltip for the tab.
25808      * @param {String} tooltip The tab's tooltip
25809      */
25810     setTooltip : function(text){
25811         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
25812             this.textEl.dom.qtip = text;
25813             this.textEl.dom.removeAttribute('title');
25814         }else{
25815             this.textEl.dom.title = text;
25816         }
25817     },
25818
25819     onTabClick : function(e){
25820         e.preventDefault();
25821         this.tabPanel.activate(this.id);
25822     },
25823
25824     onTabMouseDown : function(e){
25825         e.preventDefault();
25826         this.tabPanel.activate(this.id);
25827     },
25828
25829     getWidth : function(){
25830         return this.inner.getWidth();
25831     },
25832
25833     setWidth : function(width){
25834         var iwidth = width - this.pnode.getPadding("lr");
25835         this.inner.setWidth(iwidth);
25836         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
25837         this.pnode.setWidth(width);
25838     },
25839
25840     /**
25841      * Show or hide the tab
25842      * @param {Boolean} hidden True to hide or false to show.
25843      */
25844     setHidden : function(hidden){
25845         this.hidden = hidden;
25846         this.pnode.setStyle("display", hidden ? "none" : "");
25847     },
25848
25849     /**
25850      * Returns true if this tab is "hidden"
25851      * @return {Boolean}
25852      */
25853     isHidden : function(){
25854         return this.hidden;
25855     },
25856
25857     /**
25858      * Returns the text for this tab
25859      * @return {String}
25860      */
25861     getText : function(){
25862         return this.text;
25863     },
25864
25865     autoSize : function(){
25866         //this.el.beginMeasure();
25867         this.textEl.setWidth(1);
25868         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
25869         //this.el.endMeasure();
25870     },
25871
25872     /**
25873      * Sets the text for the tab (Note: this also sets the tooltip text)
25874      * @param {String} text The tab's text and tooltip
25875      */
25876     setText : function(text){
25877         this.text = text;
25878         this.textEl.update(text);
25879         this.setTooltip(text);
25880         if(!this.tabPanel.resizeTabs){
25881             this.autoSize();
25882         }
25883     },
25884     /**
25885      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
25886      */
25887     activate : function(){
25888         this.tabPanel.activate(this.id);
25889     },
25890
25891     /**
25892      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
25893      */
25894     disable : function(){
25895         if(this.tabPanel.active != this){
25896             this.disabled = true;
25897             this.pnode.addClass("disabled");
25898         }
25899     },
25900
25901     /**
25902      * Enables this TabPanelItem if it was previously disabled.
25903      */
25904     enable : function(){
25905         this.disabled = false;
25906         this.pnode.removeClass("disabled");
25907     },
25908
25909     /**
25910      * Sets the content for this TabPanelItem.
25911      * @param {String} content The content
25912      * @param {Boolean} loadScripts true to look for and load scripts
25913      */
25914     setContent : function(content, loadScripts){
25915         this.bodyEl.update(content, loadScripts);
25916     },
25917
25918     /**
25919      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
25920      * @return {Roo.UpdateManager} The UpdateManager
25921      */
25922     getUpdateManager : function(){
25923         return this.bodyEl.getUpdateManager();
25924     },
25925
25926     /**
25927      * Set a URL to be used to load the content for this TabPanelItem.
25928      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
25929      * @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)
25930      * @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)
25931      * @return {Roo.UpdateManager} The UpdateManager
25932      */
25933     setUrl : function(url, params, loadOnce){
25934         if(this.refreshDelegate){
25935             this.un('activate', this.refreshDelegate);
25936         }
25937         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
25938         this.on("activate", this.refreshDelegate);
25939         return this.bodyEl.getUpdateManager();
25940     },
25941
25942     /** @private */
25943     _handleRefresh : function(url, params, loadOnce){
25944         if(!loadOnce || !this.loaded){
25945             var updater = this.bodyEl.getUpdateManager();
25946             updater.update(url, params, this._setLoaded.createDelegate(this));
25947         }
25948     },
25949
25950     /**
25951      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
25952      *   Will fail silently if the setUrl method has not been called.
25953      *   This does not activate the panel, just updates its content.
25954      */
25955     refresh : function(){
25956         if(this.refreshDelegate){
25957            this.loaded = false;
25958            this.refreshDelegate();
25959         }
25960     },
25961
25962     /** @private */
25963     _setLoaded : function(){
25964         this.loaded = true;
25965     },
25966
25967     /** @private */
25968     closeClick : function(e){
25969         var o = {};
25970         e.stopEvent();
25971         this.fireEvent("beforeclose", this, o);
25972         if(o.cancel !== true){
25973             this.tabPanel.removeTab(this.id);
25974         }
25975     },
25976     /**
25977      * The text displayed in the tooltip for the close icon.
25978      * @type String
25979      */
25980     closeText : "Close this tab"
25981 });
25982
25983 /** @private */
25984 Roo.TabPanel.prototype.createStrip = function(container){
25985     var strip = document.createElement("div");
25986     strip.className = "x-tabs-wrap";
25987     container.appendChild(strip);
25988     return strip;
25989 };
25990 /** @private */
25991 Roo.TabPanel.prototype.createStripList = function(strip){
25992     // div wrapper for retard IE
25993     // returns the "tr" element.
25994     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
25995         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
25996         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
25997     return strip.firstChild.firstChild.firstChild.firstChild;
25998 };
25999 /** @private */
26000 Roo.TabPanel.prototype.createBody = function(container){
26001     var body = document.createElement("div");
26002     Roo.id(body, "tab-body");
26003     Roo.fly(body).addClass("x-tabs-body");
26004     container.appendChild(body);
26005     return body;
26006 };
26007 /** @private */
26008 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
26009     var body = Roo.getDom(id);
26010     if(!body){
26011         body = document.createElement("div");
26012         body.id = id;
26013     }
26014     Roo.fly(body).addClass("x-tabs-item-body");
26015     bodyEl.insertBefore(body, bodyEl.firstChild);
26016     return body;
26017 };
26018 /** @private */
26019 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
26020     var td = document.createElement("td");
26021     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
26022     //stripEl.appendChild(td);
26023     if(closable){
26024         td.className = "x-tabs-closable";
26025         if(!this.closeTpl){
26026             this.closeTpl = new Roo.Template(
26027                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
26028                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
26029                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
26030             );
26031         }
26032         var el = this.closeTpl.overwrite(td, {"text": text});
26033         var close = el.getElementsByTagName("div")[0];
26034         var inner = el.getElementsByTagName("em")[0];
26035         return {"el": el, "close": close, "inner": inner};
26036     } else {
26037         if(!this.tabTpl){
26038             this.tabTpl = new Roo.Template(
26039                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
26040                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
26041             );
26042         }
26043         var el = this.tabTpl.overwrite(td, {"text": text});
26044         var inner = el.getElementsByTagName("em")[0];
26045         return {"el": el, "inner": inner};
26046     }
26047 };/*
26048  * Based on:
26049  * Ext JS Library 1.1.1
26050  * Copyright(c) 2006-2007, Ext JS, LLC.
26051  *
26052  * Originally Released Under LGPL - original licence link has changed is not relivant.
26053  *
26054  * Fork - LGPL
26055  * <script type="text/javascript">
26056  */
26057
26058 /**
26059  * @class Roo.Button
26060  * @extends Roo.util.Observable
26061  * Simple Button class
26062  * @cfg {String} text The button text
26063  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
26064  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
26065  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
26066  * @cfg {Object} scope The scope of the handler
26067  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
26068  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
26069  * @cfg {Boolean} hidden True to start hidden (defaults to false)
26070  * @cfg {Boolean} disabled True to start disabled (defaults to false)
26071  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
26072  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
26073    applies if enableToggle = true)
26074  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
26075  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
26076   an {@link Roo.util.ClickRepeater} config object (defaults to false).
26077  * @constructor
26078  * Create a new button
26079  * @param {Object} config The config object
26080  */
26081 Roo.Button = function(renderTo, config)
26082 {
26083     if (!config) {
26084         config = renderTo;
26085         renderTo = config.renderTo || false;
26086     }
26087     
26088     Roo.apply(this, config);
26089     this.addEvents({
26090         /**
26091              * @event click
26092              * Fires when this button is clicked
26093              * @param {Button} this
26094              * @param {EventObject} e The click event
26095              */
26096             "click" : true,
26097         /**
26098              * @event toggle
26099              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
26100              * @param {Button} this
26101              * @param {Boolean} pressed
26102              */
26103             "toggle" : true,
26104         /**
26105              * @event mouseover
26106              * Fires when the mouse hovers over the button
26107              * @param {Button} this
26108              * @param {Event} e The event object
26109              */
26110         'mouseover' : true,
26111         /**
26112              * @event mouseout
26113              * Fires when the mouse exits the button
26114              * @param {Button} this
26115              * @param {Event} e The event object
26116              */
26117         'mouseout': true,
26118          /**
26119              * @event render
26120              * Fires when the button is rendered
26121              * @param {Button} this
26122              */
26123         'render': true
26124     });
26125     if(this.menu){
26126         this.menu = Roo.menu.MenuMgr.get(this.menu);
26127     }
26128     // register listeners first!!  - so render can be captured..
26129     Roo.util.Observable.call(this);
26130     if(renderTo){
26131         this.render(renderTo);
26132     }
26133     
26134   
26135 };
26136
26137 Roo.extend(Roo.Button, Roo.util.Observable, {
26138     /**
26139      * 
26140      */
26141     
26142     /**
26143      * Read-only. True if this button is hidden
26144      * @type Boolean
26145      */
26146     hidden : false,
26147     /**
26148      * Read-only. True if this button is disabled
26149      * @type Boolean
26150      */
26151     disabled : false,
26152     /**
26153      * Read-only. True if this button is pressed (only if enableToggle = true)
26154      * @type Boolean
26155      */
26156     pressed : false,
26157
26158     /**
26159      * @cfg {Number} tabIndex 
26160      * The DOM tabIndex for this button (defaults to undefined)
26161      */
26162     tabIndex : undefined,
26163
26164     /**
26165      * @cfg {Boolean} enableToggle
26166      * True to enable pressed/not pressed toggling (defaults to false)
26167      */
26168     enableToggle: false,
26169     /**
26170      * @cfg {Mixed} menu
26171      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
26172      */
26173     menu : undefined,
26174     /**
26175      * @cfg {String} menuAlign
26176      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
26177      */
26178     menuAlign : "tl-bl?",
26179
26180     /**
26181      * @cfg {String} iconCls
26182      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
26183      */
26184     iconCls : undefined,
26185     /**
26186      * @cfg {String} type
26187      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
26188      */
26189     type : 'button',
26190
26191     // private
26192     menuClassTarget: 'tr',
26193
26194     /**
26195      * @cfg {String} clickEvent
26196      * The type of event to map to the button's event handler (defaults to 'click')
26197      */
26198     clickEvent : 'click',
26199
26200     /**
26201      * @cfg {Boolean} handleMouseEvents
26202      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
26203      */
26204     handleMouseEvents : true,
26205
26206     /**
26207      * @cfg {String} tooltipType
26208      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
26209      */
26210     tooltipType : 'qtip',
26211
26212     /**
26213      * @cfg {String} cls
26214      * A CSS class to apply to the button's main element.
26215      */
26216     
26217     /**
26218      * @cfg {Roo.Template} template (Optional)
26219      * An {@link Roo.Template} with which to create the Button's main element. This Template must
26220      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
26221      * require code modifications if required elements (e.g. a button) aren't present.
26222      */
26223
26224     // private
26225     render : function(renderTo){
26226         var btn;
26227         if(this.hideParent){
26228             this.parentEl = Roo.get(renderTo);
26229         }
26230         if(!this.dhconfig){
26231             if(!this.template){
26232                 if(!Roo.Button.buttonTemplate){
26233                     // hideous table template
26234                     Roo.Button.buttonTemplate = new Roo.Template(
26235                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
26236                         '<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>',
26237                         "</tr></tbody></table>");
26238                 }
26239                 this.template = Roo.Button.buttonTemplate;
26240             }
26241             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
26242             var btnEl = btn.child("button:first");
26243             btnEl.on('focus', this.onFocus, this);
26244             btnEl.on('blur', this.onBlur, this);
26245             if(this.cls){
26246                 btn.addClass(this.cls);
26247             }
26248             if(this.icon){
26249                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
26250             }
26251             if(this.iconCls){
26252                 btnEl.addClass(this.iconCls);
26253                 if(!this.cls){
26254                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
26255                 }
26256             }
26257             if(this.tabIndex !== undefined){
26258                 btnEl.dom.tabIndex = this.tabIndex;
26259             }
26260             if(this.tooltip){
26261                 if(typeof this.tooltip == 'object'){
26262                     Roo.QuickTips.tips(Roo.apply({
26263                           target: btnEl.id
26264                     }, this.tooltip));
26265                 } else {
26266                     btnEl.dom[this.tooltipType] = this.tooltip;
26267                 }
26268             }
26269         }else{
26270             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
26271         }
26272         this.el = btn;
26273         if(this.id){
26274             this.el.dom.id = this.el.id = this.id;
26275         }
26276         if(this.menu){
26277             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
26278             this.menu.on("show", this.onMenuShow, this);
26279             this.menu.on("hide", this.onMenuHide, this);
26280         }
26281         btn.addClass("x-btn");
26282         if(Roo.isIE && !Roo.isIE7){
26283             this.autoWidth.defer(1, this);
26284         }else{
26285             this.autoWidth();
26286         }
26287         if(this.handleMouseEvents){
26288             btn.on("mouseover", this.onMouseOver, this);
26289             btn.on("mouseout", this.onMouseOut, this);
26290             btn.on("mousedown", this.onMouseDown, this);
26291         }
26292         btn.on(this.clickEvent, this.onClick, this);
26293         //btn.on("mouseup", this.onMouseUp, this);
26294         if(this.hidden){
26295             this.hide();
26296         }
26297         if(this.disabled){
26298             this.disable();
26299         }
26300         Roo.ButtonToggleMgr.register(this);
26301         if(this.pressed){
26302             this.el.addClass("x-btn-pressed");
26303         }
26304         if(this.repeat){
26305             var repeater = new Roo.util.ClickRepeater(btn,
26306                 typeof this.repeat == "object" ? this.repeat : {}
26307             );
26308             repeater.on("click", this.onClick,  this);
26309         }
26310         
26311         this.fireEvent('render', this);
26312         
26313     },
26314     /**
26315      * Returns the button's underlying element
26316      * @return {Roo.Element} The element
26317      */
26318     getEl : function(){
26319         return this.el;  
26320     },
26321     
26322     /**
26323      * Destroys this Button and removes any listeners.
26324      */
26325     destroy : function(){
26326         Roo.ButtonToggleMgr.unregister(this);
26327         this.el.removeAllListeners();
26328         this.purgeListeners();
26329         this.el.remove();
26330     },
26331
26332     // private
26333     autoWidth : function(){
26334         if(this.el){
26335             this.el.setWidth("auto");
26336             if(Roo.isIE7 && Roo.isStrict){
26337                 var ib = this.el.child('button');
26338                 if(ib && ib.getWidth() > 20){
26339                     ib.clip();
26340                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
26341                 }
26342             }
26343             if(this.minWidth){
26344                 if(this.hidden){
26345                     this.el.beginMeasure();
26346                 }
26347                 if(this.el.getWidth() < this.minWidth){
26348                     this.el.setWidth(this.minWidth);
26349                 }
26350                 if(this.hidden){
26351                     this.el.endMeasure();
26352                 }
26353             }
26354         }
26355     },
26356
26357     /**
26358      * Assigns this button's click handler
26359      * @param {Function} handler The function to call when the button is clicked
26360      * @param {Object} scope (optional) Scope for the function passed in
26361      */
26362     setHandler : function(handler, scope){
26363         this.handler = handler;
26364         this.scope = scope;  
26365     },
26366     
26367     /**
26368      * Sets this button's text
26369      * @param {String} text The button text
26370      */
26371     setText : function(text){
26372         this.text = text;
26373         if(this.el){
26374             this.el.child("td.x-btn-center button.x-btn-text").update(text);
26375         }
26376         this.autoWidth();
26377     },
26378     
26379     /**
26380      * Gets the text for this button
26381      * @return {String} The button text
26382      */
26383     getText : function(){
26384         return this.text;  
26385     },
26386     
26387     /**
26388      * Show this button
26389      */
26390     show: function(){
26391         this.hidden = false;
26392         if(this.el){
26393             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
26394         }
26395     },
26396     
26397     /**
26398      * Hide this button
26399      */
26400     hide: function(){
26401         this.hidden = true;
26402         if(this.el){
26403             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
26404         }
26405     },
26406     
26407     /**
26408      * Convenience function for boolean show/hide
26409      * @param {Boolean} visible True to show, false to hide
26410      */
26411     setVisible: function(visible){
26412         if(visible) {
26413             this.show();
26414         }else{
26415             this.hide();
26416         }
26417     },
26418     
26419     /**
26420      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
26421      * @param {Boolean} state (optional) Force a particular state
26422      */
26423     toggle : function(state){
26424         state = state === undefined ? !this.pressed : state;
26425         if(state != this.pressed){
26426             if(state){
26427                 this.el.addClass("x-btn-pressed");
26428                 this.pressed = true;
26429                 this.fireEvent("toggle", this, true);
26430             }else{
26431                 this.el.removeClass("x-btn-pressed");
26432                 this.pressed = false;
26433                 this.fireEvent("toggle", this, false);
26434             }
26435             if(this.toggleHandler){
26436                 this.toggleHandler.call(this.scope || this, this, state);
26437             }
26438         }
26439     },
26440     
26441     /**
26442      * Focus the button
26443      */
26444     focus : function(){
26445         this.el.child('button:first').focus();
26446     },
26447     
26448     /**
26449      * Disable this button
26450      */
26451     disable : function(){
26452         if(this.el){
26453             this.el.addClass("x-btn-disabled");
26454         }
26455         this.disabled = true;
26456     },
26457     
26458     /**
26459      * Enable this button
26460      */
26461     enable : function(){
26462         if(this.el){
26463             this.el.removeClass("x-btn-disabled");
26464         }
26465         this.disabled = false;
26466     },
26467
26468     /**
26469      * Convenience function for boolean enable/disable
26470      * @param {Boolean} enabled True to enable, false to disable
26471      */
26472     setDisabled : function(v){
26473         this[v !== true ? "enable" : "disable"]();
26474     },
26475
26476     // private
26477     onClick : function(e){
26478         if(e){
26479             e.preventDefault();
26480         }
26481         if(e.button != 0){
26482             return;
26483         }
26484         if(!this.disabled){
26485             if(this.enableToggle){
26486                 this.toggle();
26487             }
26488             if(this.menu && !this.menu.isVisible()){
26489                 this.menu.show(this.el, this.menuAlign);
26490             }
26491             this.fireEvent("click", this, e);
26492             if(this.handler){
26493                 this.el.removeClass("x-btn-over");
26494                 this.handler.call(this.scope || this, this, e);
26495             }
26496         }
26497     },
26498     // private
26499     onMouseOver : function(e){
26500         if(!this.disabled){
26501             this.el.addClass("x-btn-over");
26502             this.fireEvent('mouseover', this, e);
26503         }
26504     },
26505     // private
26506     onMouseOut : function(e){
26507         if(!e.within(this.el,  true)){
26508             this.el.removeClass("x-btn-over");
26509             this.fireEvent('mouseout', this, e);
26510         }
26511     },
26512     // private
26513     onFocus : function(e){
26514         if(!this.disabled){
26515             this.el.addClass("x-btn-focus");
26516         }
26517     },
26518     // private
26519     onBlur : function(e){
26520         this.el.removeClass("x-btn-focus");
26521     },
26522     // private
26523     onMouseDown : function(e){
26524         if(!this.disabled && e.button == 0){
26525             this.el.addClass("x-btn-click");
26526             Roo.get(document).on('mouseup', this.onMouseUp, this);
26527         }
26528     },
26529     // private
26530     onMouseUp : function(e){
26531         if(e.button == 0){
26532             this.el.removeClass("x-btn-click");
26533             Roo.get(document).un('mouseup', this.onMouseUp, this);
26534         }
26535     },
26536     // private
26537     onMenuShow : function(e){
26538         this.el.addClass("x-btn-menu-active");
26539     },
26540     // private
26541     onMenuHide : function(e){
26542         this.el.removeClass("x-btn-menu-active");
26543     }   
26544 });
26545
26546 // Private utility class used by Button
26547 Roo.ButtonToggleMgr = function(){
26548    var groups = {};
26549    
26550    function toggleGroup(btn, state){
26551        if(state){
26552            var g = groups[btn.toggleGroup];
26553            for(var i = 0, l = g.length; i < l; i++){
26554                if(g[i] != btn){
26555                    g[i].toggle(false);
26556                }
26557            }
26558        }
26559    }
26560    
26561    return {
26562        register : function(btn){
26563            if(!btn.toggleGroup){
26564                return;
26565            }
26566            var g = groups[btn.toggleGroup];
26567            if(!g){
26568                g = groups[btn.toggleGroup] = [];
26569            }
26570            g.push(btn);
26571            btn.on("toggle", toggleGroup);
26572        },
26573        
26574        unregister : function(btn){
26575            if(!btn.toggleGroup){
26576                return;
26577            }
26578            var g = groups[btn.toggleGroup];
26579            if(g){
26580                g.remove(btn);
26581                btn.un("toggle", toggleGroup);
26582            }
26583        }
26584    };
26585 }();/*
26586  * Based on:
26587  * Ext JS Library 1.1.1
26588  * Copyright(c) 2006-2007, Ext JS, LLC.
26589  *
26590  * Originally Released Under LGPL - original licence link has changed is not relivant.
26591  *
26592  * Fork - LGPL
26593  * <script type="text/javascript">
26594  */
26595  
26596 /**
26597  * @class Roo.SplitButton
26598  * @extends Roo.Button
26599  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
26600  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
26601  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
26602  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
26603  * @cfg {String} arrowTooltip The title attribute of the arrow
26604  * @constructor
26605  * Create a new menu button
26606  * @param {String/HTMLElement/Element} renderTo The element to append the button to
26607  * @param {Object} config The config object
26608  */
26609 Roo.SplitButton = function(renderTo, config){
26610     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
26611     /**
26612      * @event arrowclick
26613      * Fires when this button's arrow is clicked
26614      * @param {SplitButton} this
26615      * @param {EventObject} e The click event
26616      */
26617     this.addEvents({"arrowclick":true});
26618 };
26619
26620 Roo.extend(Roo.SplitButton, Roo.Button, {
26621     render : function(renderTo){
26622         // this is one sweet looking template!
26623         var tpl = new Roo.Template(
26624             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
26625             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
26626             '<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>',
26627             "</tbody></table></td><td>",
26628             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
26629             '<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>',
26630             "</tbody></table></td></tr></table>"
26631         );
26632         var btn = tpl.append(renderTo, [this.text, this.type], true);
26633         var btnEl = btn.child("button");
26634         if(this.cls){
26635             btn.addClass(this.cls);
26636         }
26637         if(this.icon){
26638             btnEl.setStyle('background-image', 'url(' +this.icon +')');
26639         }
26640         if(this.iconCls){
26641             btnEl.addClass(this.iconCls);
26642             if(!this.cls){
26643                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
26644             }
26645         }
26646         this.el = btn;
26647         if(this.handleMouseEvents){
26648             btn.on("mouseover", this.onMouseOver, this);
26649             btn.on("mouseout", this.onMouseOut, this);
26650             btn.on("mousedown", this.onMouseDown, this);
26651             btn.on("mouseup", this.onMouseUp, this);
26652         }
26653         btn.on(this.clickEvent, this.onClick, this);
26654         if(this.tooltip){
26655             if(typeof this.tooltip == 'object'){
26656                 Roo.QuickTips.tips(Roo.apply({
26657                       target: btnEl.id
26658                 }, this.tooltip));
26659             } else {
26660                 btnEl.dom[this.tooltipType] = this.tooltip;
26661             }
26662         }
26663         if(this.arrowTooltip){
26664             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
26665         }
26666         if(this.hidden){
26667             this.hide();
26668         }
26669         if(this.disabled){
26670             this.disable();
26671         }
26672         if(this.pressed){
26673             this.el.addClass("x-btn-pressed");
26674         }
26675         if(Roo.isIE && !Roo.isIE7){
26676             this.autoWidth.defer(1, this);
26677         }else{
26678             this.autoWidth();
26679         }
26680         if(this.menu){
26681             this.menu.on("show", this.onMenuShow, this);
26682             this.menu.on("hide", this.onMenuHide, this);
26683         }
26684         this.fireEvent('render', this);
26685     },
26686
26687     // private
26688     autoWidth : function(){
26689         if(this.el){
26690             var tbl = this.el.child("table:first");
26691             var tbl2 = this.el.child("table:last");
26692             this.el.setWidth("auto");
26693             tbl.setWidth("auto");
26694             if(Roo.isIE7 && Roo.isStrict){
26695                 var ib = this.el.child('button:first');
26696                 if(ib && ib.getWidth() > 20){
26697                     ib.clip();
26698                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
26699                 }
26700             }
26701             if(this.minWidth){
26702                 if(this.hidden){
26703                     this.el.beginMeasure();
26704                 }
26705                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
26706                     tbl.setWidth(this.minWidth-tbl2.getWidth());
26707                 }
26708                 if(this.hidden){
26709                     this.el.endMeasure();
26710                 }
26711             }
26712             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
26713         } 
26714     },
26715     /**
26716      * Sets this button's click handler
26717      * @param {Function} handler The function to call when the button is clicked
26718      * @param {Object} scope (optional) Scope for the function passed above
26719      */
26720     setHandler : function(handler, scope){
26721         this.handler = handler;
26722         this.scope = scope;  
26723     },
26724     
26725     /**
26726      * Sets this button's arrow click handler
26727      * @param {Function} handler The function to call when the arrow is clicked
26728      * @param {Object} scope (optional) Scope for the function passed above
26729      */
26730     setArrowHandler : function(handler, scope){
26731         this.arrowHandler = handler;
26732         this.scope = scope;  
26733     },
26734     
26735     /**
26736      * Focus the button
26737      */
26738     focus : function(){
26739         if(this.el){
26740             this.el.child("button:first").focus();
26741         }
26742     },
26743
26744     // private
26745     onClick : function(e){
26746         e.preventDefault();
26747         if(!this.disabled){
26748             if(e.getTarget(".x-btn-menu-arrow-wrap")){
26749                 if(this.menu && !this.menu.isVisible()){
26750                     this.menu.show(this.el, this.menuAlign);
26751                 }
26752                 this.fireEvent("arrowclick", this, e);
26753                 if(this.arrowHandler){
26754                     this.arrowHandler.call(this.scope || this, this, e);
26755                 }
26756             }else{
26757                 this.fireEvent("click", this, e);
26758                 if(this.handler){
26759                     this.handler.call(this.scope || this, this, e);
26760                 }
26761             }
26762         }
26763     },
26764     // private
26765     onMouseDown : function(e){
26766         if(!this.disabled){
26767             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
26768         }
26769     },
26770     // private
26771     onMouseUp : function(e){
26772         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
26773     }   
26774 });
26775
26776
26777 // backwards compat
26778 Roo.MenuButton = Roo.SplitButton;/*
26779  * Based on:
26780  * Ext JS Library 1.1.1
26781  * Copyright(c) 2006-2007, Ext JS, LLC.
26782  *
26783  * Originally Released Under LGPL - original licence link has changed is not relivant.
26784  *
26785  * Fork - LGPL
26786  * <script type="text/javascript">
26787  */
26788
26789 /**
26790  * @class Roo.Toolbar
26791  * Basic Toolbar class.
26792  * @constructor
26793  * Creates a new Toolbar
26794  * @param {Object} container The config object
26795  */ 
26796 Roo.Toolbar = function(container, buttons, config)
26797 {
26798     /// old consturctor format still supported..
26799     if(container instanceof Array){ // omit the container for later rendering
26800         buttons = container;
26801         config = buttons;
26802         container = null;
26803     }
26804     if (typeof(container) == 'object' && container.xtype) {
26805         config = container;
26806         container = config.container;
26807         buttons = config.buttons || []; // not really - use items!!
26808     }
26809     var xitems = [];
26810     if (config && config.items) {
26811         xitems = config.items;
26812         delete config.items;
26813     }
26814     Roo.apply(this, config);
26815     this.buttons = buttons;
26816     
26817     if(container){
26818         this.render(container);
26819     }
26820     this.xitems = xitems;
26821     Roo.each(xitems, function(b) {
26822         this.add(b);
26823     }, this);
26824     
26825 };
26826
26827 Roo.Toolbar.prototype = {
26828     /**
26829      * @cfg {Array} items
26830      * array of button configs or elements to add (will be converted to a MixedCollection)
26831      */
26832     
26833     /**
26834      * @cfg {String/HTMLElement/Element} container
26835      * The id or element that will contain the toolbar
26836      */
26837     // private
26838     render : function(ct){
26839         this.el = Roo.get(ct);
26840         if(this.cls){
26841             this.el.addClass(this.cls);
26842         }
26843         // using a table allows for vertical alignment
26844         // 100% width is needed by Safari...
26845         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
26846         this.tr = this.el.child("tr", true);
26847         var autoId = 0;
26848         this.items = new Roo.util.MixedCollection(false, function(o){
26849             return o.id || ("item" + (++autoId));
26850         });
26851         if(this.buttons){
26852             this.add.apply(this, this.buttons);
26853             delete this.buttons;
26854         }
26855     },
26856
26857     /**
26858      * Adds element(s) to the toolbar -- this function takes a variable number of 
26859      * arguments of mixed type and adds them to the toolbar.
26860      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
26861      * <ul>
26862      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
26863      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
26864      * <li>Field: Any form field (equivalent to {@link #addField})</li>
26865      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
26866      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
26867      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
26868      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
26869      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
26870      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
26871      * </ul>
26872      * @param {Mixed} arg2
26873      * @param {Mixed} etc.
26874      */
26875     add : function(){
26876         var a = arguments, l = a.length;
26877         for(var i = 0; i < l; i++){
26878             this._add(a[i]);
26879         }
26880     },
26881     // private..
26882     _add : function(el) {
26883         
26884         if (el.xtype) {
26885             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
26886         }
26887         
26888         if (el.applyTo){ // some kind of form field
26889             return this.addField(el);
26890         } 
26891         if (el.render){ // some kind of Toolbar.Item
26892             return this.addItem(el);
26893         }
26894         if (typeof el == "string"){ // string
26895             if(el == "separator" || el == "-"){
26896                 return this.addSeparator();
26897             }
26898             if (el == " "){
26899                 return this.addSpacer();
26900             }
26901             if(el == "->"){
26902                 return this.addFill();
26903             }
26904             return this.addText(el);
26905             
26906         }
26907         if(el.tagName){ // element
26908             return this.addElement(el);
26909         }
26910         if(typeof el == "object"){ // must be button config?
26911             return this.addButton(el);
26912         }
26913         // and now what?!?!
26914         return false;
26915         
26916     },
26917     
26918     /**
26919      * Add an Xtype element
26920      * @param {Object} xtype Xtype Object
26921      * @return {Object} created Object
26922      */
26923     addxtype : function(e){
26924         return this.add(e);  
26925     },
26926     
26927     /**
26928      * Returns the Element for this toolbar.
26929      * @return {Roo.Element}
26930      */
26931     getEl : function(){
26932         return this.el;  
26933     },
26934     
26935     /**
26936      * Adds a separator
26937      * @return {Roo.Toolbar.Item} The separator item
26938      */
26939     addSeparator : function(){
26940         return this.addItem(new Roo.Toolbar.Separator());
26941     },
26942
26943     /**
26944      * Adds a spacer element
26945      * @return {Roo.Toolbar.Spacer} The spacer item
26946      */
26947     addSpacer : function(){
26948         return this.addItem(new Roo.Toolbar.Spacer());
26949     },
26950
26951     /**
26952      * Adds a fill element that forces subsequent additions to the right side of the toolbar
26953      * @return {Roo.Toolbar.Fill} The fill item
26954      */
26955     addFill : function(){
26956         return this.addItem(new Roo.Toolbar.Fill());
26957     },
26958
26959     /**
26960      * Adds any standard HTML element to the toolbar
26961      * @param {String/HTMLElement/Element} el The element or id of the element to add
26962      * @return {Roo.Toolbar.Item} The element's item
26963      */
26964     addElement : function(el){
26965         return this.addItem(new Roo.Toolbar.Item(el));
26966     },
26967     /**
26968      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
26969      * @type Roo.util.MixedCollection  
26970      */
26971     items : false,
26972      
26973     /**
26974      * Adds any Toolbar.Item or subclass
26975      * @param {Roo.Toolbar.Item} item
26976      * @return {Roo.Toolbar.Item} The item
26977      */
26978     addItem : function(item){
26979         var td = this.nextBlock();
26980         item.render(td);
26981         this.items.add(item);
26982         return item;
26983     },
26984     
26985     /**
26986      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
26987      * @param {Object/Array} config A button config or array of configs
26988      * @return {Roo.Toolbar.Button/Array}
26989      */
26990     addButton : function(config){
26991         if(config instanceof Array){
26992             var buttons = [];
26993             for(var i = 0, len = config.length; i < len; i++) {
26994                 buttons.push(this.addButton(config[i]));
26995             }
26996             return buttons;
26997         }
26998         var b = config;
26999         if(!(config instanceof Roo.Toolbar.Button)){
27000             b = config.split ?
27001                 new Roo.Toolbar.SplitButton(config) :
27002                 new Roo.Toolbar.Button(config);
27003         }
27004         var td = this.nextBlock();
27005         b.render(td);
27006         this.items.add(b);
27007         return b;
27008     },
27009     
27010     /**
27011      * Adds text to the toolbar
27012      * @param {String} text The text to add
27013      * @return {Roo.Toolbar.Item} The element's item
27014      */
27015     addText : function(text){
27016         return this.addItem(new Roo.Toolbar.TextItem(text));
27017     },
27018     
27019     /**
27020      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
27021      * @param {Number} index The index where the item is to be inserted
27022      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
27023      * @return {Roo.Toolbar.Button/Item}
27024      */
27025     insertButton : function(index, item){
27026         if(item instanceof Array){
27027             var buttons = [];
27028             for(var i = 0, len = item.length; i < len; i++) {
27029                buttons.push(this.insertButton(index + i, item[i]));
27030             }
27031             return buttons;
27032         }
27033         if (!(item instanceof Roo.Toolbar.Button)){
27034            item = new Roo.Toolbar.Button(item);
27035         }
27036         var td = document.createElement("td");
27037         this.tr.insertBefore(td, this.tr.childNodes[index]);
27038         item.render(td);
27039         this.items.insert(index, item);
27040         return item;
27041     },
27042     
27043     /**
27044      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
27045      * @param {Object} config
27046      * @return {Roo.Toolbar.Item} The element's item
27047      */
27048     addDom : function(config, returnEl){
27049         var td = this.nextBlock();
27050         Roo.DomHelper.overwrite(td, config);
27051         var ti = new Roo.Toolbar.Item(td.firstChild);
27052         ti.render(td);
27053         this.items.add(ti);
27054         return ti;
27055     },
27056
27057     /**
27058      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
27059      * @type Roo.util.MixedCollection  
27060      */
27061     fields : false,
27062     
27063     /**
27064      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
27065      * Note: the field should not have been rendered yet. For a field that has already been
27066      * rendered, use {@link #addElement}.
27067      * @param {Roo.form.Field} field
27068      * @return {Roo.ToolbarItem}
27069      */
27070      
27071       
27072     addField : function(field) {
27073         if (!this.fields) {
27074             var autoId = 0;
27075             this.fields = new Roo.util.MixedCollection(false, function(o){
27076                 return o.id || ("item" + (++autoId));
27077             });
27078
27079         }
27080         
27081         var td = this.nextBlock();
27082         field.render(td);
27083         var ti = new Roo.Toolbar.Item(td.firstChild);
27084         ti.render(td);
27085         this.items.add(ti);
27086         this.fields.add(field);
27087         return ti;
27088     },
27089     /**
27090      * Hide the toolbar
27091      * @method hide
27092      */
27093      
27094       
27095     hide : function()
27096     {
27097         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
27098         this.el.child('div').hide();
27099     },
27100     /**
27101      * Show the toolbar
27102      * @method show
27103      */
27104     show : function()
27105     {
27106         this.el.child('div').show();
27107     },
27108       
27109     // private
27110     nextBlock : function(){
27111         var td = document.createElement("td");
27112         this.tr.appendChild(td);
27113         return td;
27114     },
27115
27116     // private
27117     destroy : function(){
27118         if(this.items){ // rendered?
27119             Roo.destroy.apply(Roo, this.items.items);
27120         }
27121         if(this.fields){ // rendered?
27122             Roo.destroy.apply(Roo, this.fields.items);
27123         }
27124         Roo.Element.uncache(this.el, this.tr);
27125     }
27126 };
27127
27128 /**
27129  * @class Roo.Toolbar.Item
27130  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
27131  * @constructor
27132  * Creates a new Item
27133  * @param {HTMLElement} el 
27134  */
27135 Roo.Toolbar.Item = function(el){
27136     this.el = Roo.getDom(el);
27137     this.id = Roo.id(this.el);
27138     this.hidden = false;
27139 };
27140
27141 Roo.Toolbar.Item.prototype = {
27142     
27143     /**
27144      * Get this item's HTML Element
27145      * @return {HTMLElement}
27146      */
27147     getEl : function(){
27148        return this.el;  
27149     },
27150
27151     // private
27152     render : function(td){
27153         this.td = td;
27154         td.appendChild(this.el);
27155     },
27156     
27157     /**
27158      * Removes and destroys this item.
27159      */
27160     destroy : function(){
27161         this.td.parentNode.removeChild(this.td);
27162     },
27163     
27164     /**
27165      * Shows this item.
27166      */
27167     show: function(){
27168         this.hidden = false;
27169         this.td.style.display = "";
27170     },
27171     
27172     /**
27173      * Hides this item.
27174      */
27175     hide: function(){
27176         this.hidden = true;
27177         this.td.style.display = "none";
27178     },
27179     
27180     /**
27181      * Convenience function for boolean show/hide.
27182      * @param {Boolean} visible true to show/false to hide
27183      */
27184     setVisible: function(visible){
27185         if(visible) {
27186             this.show();
27187         }else{
27188             this.hide();
27189         }
27190     },
27191     
27192     /**
27193      * Try to focus this item.
27194      */
27195     focus : function(){
27196         Roo.fly(this.el).focus();
27197     },
27198     
27199     /**
27200      * Disables this item.
27201      */
27202     disable : function(){
27203         Roo.fly(this.td).addClass("x-item-disabled");
27204         this.disabled = true;
27205         this.el.disabled = true;
27206     },
27207     
27208     /**
27209      * Enables this item.
27210      */
27211     enable : function(){
27212         Roo.fly(this.td).removeClass("x-item-disabled");
27213         this.disabled = false;
27214         this.el.disabled = false;
27215     }
27216 };
27217
27218
27219 /**
27220  * @class Roo.Toolbar.Separator
27221  * @extends Roo.Toolbar.Item
27222  * A simple toolbar separator class
27223  * @constructor
27224  * Creates a new Separator
27225  */
27226 Roo.Toolbar.Separator = function(){
27227     var s = document.createElement("span");
27228     s.className = "ytb-sep";
27229     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
27230 };
27231 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
27232     enable:Roo.emptyFn,
27233     disable:Roo.emptyFn,
27234     focus:Roo.emptyFn
27235 });
27236
27237 /**
27238  * @class Roo.Toolbar.Spacer
27239  * @extends Roo.Toolbar.Item
27240  * A simple element that adds extra horizontal space to a toolbar.
27241  * @constructor
27242  * Creates a new Spacer
27243  */
27244 Roo.Toolbar.Spacer = function(){
27245     var s = document.createElement("div");
27246     s.className = "ytb-spacer";
27247     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
27248 };
27249 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
27250     enable:Roo.emptyFn,
27251     disable:Roo.emptyFn,
27252     focus:Roo.emptyFn
27253 });
27254
27255 /**
27256  * @class Roo.Toolbar.Fill
27257  * @extends Roo.Toolbar.Spacer
27258  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
27259  * @constructor
27260  * Creates a new Spacer
27261  */
27262 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
27263     // private
27264     render : function(td){
27265         td.style.width = '100%';
27266         Roo.Toolbar.Fill.superclass.render.call(this, td);
27267     }
27268 });
27269
27270 /**
27271  * @class Roo.Toolbar.TextItem
27272  * @extends Roo.Toolbar.Item
27273  * A simple class that renders text directly into a toolbar.
27274  * @constructor
27275  * Creates a new TextItem
27276  * @param {String} text
27277  */
27278 Roo.Toolbar.TextItem = function(text){
27279     if (typeof(text) == 'object') {
27280         text = text.text;
27281     }
27282     var s = document.createElement("span");
27283     s.className = "ytb-text";
27284     s.innerHTML = text;
27285     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
27286 };
27287 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
27288     enable:Roo.emptyFn,
27289     disable:Roo.emptyFn,
27290     focus:Roo.emptyFn
27291 });
27292
27293 /**
27294  * @class Roo.Toolbar.Button
27295  * @extends Roo.Button
27296  * A button that renders into a toolbar.
27297  * @constructor
27298  * Creates a new Button
27299  * @param {Object} config A standard {@link Roo.Button} config object
27300  */
27301 Roo.Toolbar.Button = function(config){
27302     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
27303 };
27304 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
27305     render : function(td){
27306         this.td = td;
27307         Roo.Toolbar.Button.superclass.render.call(this, td);
27308     },
27309     
27310     /**
27311      * Removes and destroys this button
27312      */
27313     destroy : function(){
27314         Roo.Toolbar.Button.superclass.destroy.call(this);
27315         this.td.parentNode.removeChild(this.td);
27316     },
27317     
27318     /**
27319      * Shows this button
27320      */
27321     show: function(){
27322         this.hidden = false;
27323         this.td.style.display = "";
27324     },
27325     
27326     /**
27327      * Hides this button
27328      */
27329     hide: function(){
27330         this.hidden = true;
27331         this.td.style.display = "none";
27332     },
27333
27334     /**
27335      * Disables this item
27336      */
27337     disable : function(){
27338         Roo.fly(this.td).addClass("x-item-disabled");
27339         this.disabled = true;
27340     },
27341
27342     /**
27343      * Enables this item
27344      */
27345     enable : function(){
27346         Roo.fly(this.td).removeClass("x-item-disabled");
27347         this.disabled = false;
27348     }
27349 });
27350 // backwards compat
27351 Roo.ToolbarButton = Roo.Toolbar.Button;
27352
27353 /**
27354  * @class Roo.Toolbar.SplitButton
27355  * @extends Roo.SplitButton
27356  * A menu button that renders into a toolbar.
27357  * @constructor
27358  * Creates a new SplitButton
27359  * @param {Object} config A standard {@link Roo.SplitButton} config object
27360  */
27361 Roo.Toolbar.SplitButton = function(config){
27362     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
27363 };
27364 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
27365     render : function(td){
27366         this.td = td;
27367         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
27368     },
27369     
27370     /**
27371      * Removes and destroys this button
27372      */
27373     destroy : function(){
27374         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
27375         this.td.parentNode.removeChild(this.td);
27376     },
27377     
27378     /**
27379      * Shows this button
27380      */
27381     show: function(){
27382         this.hidden = false;
27383         this.td.style.display = "";
27384     },
27385     
27386     /**
27387      * Hides this button
27388      */
27389     hide: function(){
27390         this.hidden = true;
27391         this.td.style.display = "none";
27392     }
27393 });
27394
27395 // backwards compat
27396 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
27397  * Based on:
27398  * Ext JS Library 1.1.1
27399  * Copyright(c) 2006-2007, Ext JS, LLC.
27400  *
27401  * Originally Released Under LGPL - original licence link has changed is not relivant.
27402  *
27403  * Fork - LGPL
27404  * <script type="text/javascript">
27405  */
27406  
27407 /**
27408  * @class Roo.PagingToolbar
27409  * @extends Roo.Toolbar
27410  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27411  * @constructor
27412  * Create a new PagingToolbar
27413  * @param {Object} config The config object
27414  */
27415 Roo.PagingToolbar = function(el, ds, config)
27416 {
27417     // old args format still supported... - xtype is prefered..
27418     if (typeof(el) == 'object' && el.xtype) {
27419         // created from xtype...
27420         config = el;
27421         ds = el.dataSource;
27422         el = config.container;
27423     }
27424     var items = [];
27425     if (config.items) {
27426         items = config.items;
27427         config.items = [];
27428     }
27429     
27430     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
27431     this.ds = ds;
27432     this.cursor = 0;
27433     this.renderButtons(this.el);
27434     this.bind(ds);
27435     
27436     // supprot items array.
27437    
27438     Roo.each(items, function(e) {
27439         this.add(Roo.factory(e));
27440     },this);
27441     
27442 };
27443
27444 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
27445     /**
27446      * @cfg {Roo.data.Store} dataSource
27447      * The underlying data store providing the paged data
27448      */
27449     /**
27450      * @cfg {String/HTMLElement/Element} container
27451      * container The id or element that will contain the toolbar
27452      */
27453     /**
27454      * @cfg {Boolean} displayInfo
27455      * True to display the displayMsg (defaults to false)
27456      */
27457     /**
27458      * @cfg {Number} pageSize
27459      * The number of records to display per page (defaults to 20)
27460      */
27461     pageSize: 20,
27462     /**
27463      * @cfg {String} displayMsg
27464      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27465      */
27466     displayMsg : 'Displaying {0} - {1} of {2}',
27467     /**
27468      * @cfg {String} emptyMsg
27469      * The message to display when no records are found (defaults to "No data to display")
27470      */
27471     emptyMsg : 'No data to display',
27472     /**
27473      * Customizable piece of the default paging text (defaults to "Page")
27474      * @type String
27475      */
27476     beforePageText : "Page",
27477     /**
27478      * Customizable piece of the default paging text (defaults to "of %0")
27479      * @type String
27480      */
27481     afterPageText : "of {0}",
27482     /**
27483      * Customizable piece of the default paging text (defaults to "First Page")
27484      * @type String
27485      */
27486     firstText : "First Page",
27487     /**
27488      * Customizable piece of the default paging text (defaults to "Previous Page")
27489      * @type String
27490      */
27491     prevText : "Previous Page",
27492     /**
27493      * Customizable piece of the default paging text (defaults to "Next Page")
27494      * @type String
27495      */
27496     nextText : "Next Page",
27497     /**
27498      * Customizable piece of the default paging text (defaults to "Last Page")
27499      * @type String
27500      */
27501     lastText : "Last Page",
27502     /**
27503      * Customizable piece of the default paging text (defaults to "Refresh")
27504      * @type String
27505      */
27506     refreshText : "Refresh",
27507
27508     // private
27509     renderButtons : function(el){
27510         Roo.PagingToolbar.superclass.render.call(this, el);
27511         this.first = this.addButton({
27512             tooltip: this.firstText,
27513             cls: "x-btn-icon x-grid-page-first",
27514             disabled: true,
27515             handler: this.onClick.createDelegate(this, ["first"])
27516         });
27517         this.prev = this.addButton({
27518             tooltip: this.prevText,
27519             cls: "x-btn-icon x-grid-page-prev",
27520             disabled: true,
27521             handler: this.onClick.createDelegate(this, ["prev"])
27522         });
27523         //this.addSeparator();
27524         this.add(this.beforePageText);
27525         this.field = Roo.get(this.addDom({
27526            tag: "input",
27527            type: "text",
27528            size: "3",
27529            value: "1",
27530            cls: "x-grid-page-number"
27531         }).el);
27532         this.field.on("keydown", this.onPagingKeydown, this);
27533         this.field.on("focus", function(){this.dom.select();});
27534         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
27535         this.field.setHeight(18);
27536         //this.addSeparator();
27537         this.next = this.addButton({
27538             tooltip: this.nextText,
27539             cls: "x-btn-icon x-grid-page-next",
27540             disabled: true,
27541             handler: this.onClick.createDelegate(this, ["next"])
27542         });
27543         this.last = this.addButton({
27544             tooltip: this.lastText,
27545             cls: "x-btn-icon x-grid-page-last",
27546             disabled: true,
27547             handler: this.onClick.createDelegate(this, ["last"])
27548         });
27549         //this.addSeparator();
27550         this.loading = this.addButton({
27551             tooltip: this.refreshText,
27552             cls: "x-btn-icon x-grid-loading",
27553             handler: this.onClick.createDelegate(this, ["refresh"])
27554         });
27555
27556         if(this.displayInfo){
27557             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
27558         }
27559     },
27560
27561     // private
27562     updateInfo : function(){
27563         if(this.displayEl){
27564             var count = this.ds.getCount();
27565             var msg = count == 0 ?
27566                 this.emptyMsg :
27567                 String.format(
27568                     this.displayMsg,
27569                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27570                 );
27571             this.displayEl.update(msg);
27572         }
27573     },
27574
27575     // private
27576     onLoad : function(ds, r, o){
27577        this.cursor = o.params ? o.params.start : 0;
27578        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
27579
27580        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
27581        this.field.dom.value = ap;
27582        this.first.setDisabled(ap == 1);
27583        this.prev.setDisabled(ap == 1);
27584        this.next.setDisabled(ap == ps);
27585        this.last.setDisabled(ap == ps);
27586        this.loading.enable();
27587        this.updateInfo();
27588     },
27589
27590     // private
27591     getPageData : function(){
27592         var total = this.ds.getTotalCount();
27593         return {
27594             total : total,
27595             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27596             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27597         };
27598     },
27599
27600     // private
27601     onLoadError : function(){
27602         this.loading.enable();
27603     },
27604
27605     // private
27606     onPagingKeydown : function(e){
27607         var k = e.getKey();
27608         var d = this.getPageData();
27609         if(k == e.RETURN){
27610             var v = this.field.dom.value, pageNum;
27611             if(!v || isNaN(pageNum = parseInt(v, 10))){
27612                 this.field.dom.value = d.activePage;
27613                 return;
27614             }
27615             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27616             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27617             e.stopEvent();
27618         }
27619         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))
27620         {
27621           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27622           this.field.dom.value = pageNum;
27623           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27624           e.stopEvent();
27625         }
27626         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27627         {
27628           var v = this.field.dom.value, pageNum; 
27629           var increment = (e.shiftKey) ? 10 : 1;
27630           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27631             increment *= -1;
27632           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27633             this.field.dom.value = d.activePage;
27634             return;
27635           }
27636           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27637           {
27638             this.field.dom.value = parseInt(v, 10) + increment;
27639             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27640             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27641           }
27642           e.stopEvent();
27643         }
27644     },
27645
27646     // private
27647     beforeLoad : function(){
27648         if(this.loading){
27649             this.loading.disable();
27650         }
27651     },
27652
27653     // private
27654     onClick : function(which){
27655         var ds = this.ds;
27656         switch(which){
27657             case "first":
27658                 ds.load({params:{start: 0, limit: this.pageSize}});
27659             break;
27660             case "prev":
27661                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27662             break;
27663             case "next":
27664                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27665             break;
27666             case "last":
27667                 var total = ds.getTotalCount();
27668                 var extra = total % this.pageSize;
27669                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27670                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27671             break;
27672             case "refresh":
27673                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27674             break;
27675         }
27676     },
27677
27678     /**
27679      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27680      * @param {Roo.data.Store} store The data store to unbind
27681      */
27682     unbind : function(ds){
27683         ds.un("beforeload", this.beforeLoad, this);
27684         ds.un("load", this.onLoad, this);
27685         ds.un("loadexception", this.onLoadError, this);
27686         ds.un("remove", this.updateInfo, this);
27687         ds.un("add", this.updateInfo, this);
27688         this.ds = undefined;
27689     },
27690
27691     /**
27692      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27693      * @param {Roo.data.Store} store The data store to bind
27694      */
27695     bind : function(ds){
27696         ds.on("beforeload", this.beforeLoad, this);
27697         ds.on("load", this.onLoad, this);
27698         ds.on("loadexception", this.onLoadError, this);
27699         ds.on("remove", this.updateInfo, this);
27700         ds.on("add", this.updateInfo, this);
27701         this.ds = ds;
27702     }
27703 });/*
27704  * Based on:
27705  * Ext JS Library 1.1.1
27706  * Copyright(c) 2006-2007, Ext JS, LLC.
27707  *
27708  * Originally Released Under LGPL - original licence link has changed is not relivant.
27709  *
27710  * Fork - LGPL
27711  * <script type="text/javascript">
27712  */
27713
27714 /**
27715  * @class Roo.Resizable
27716  * @extends Roo.util.Observable
27717  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
27718  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
27719  * 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
27720  * the element will be wrapped for you automatically.</p>
27721  * <p>Here is the list of valid resize handles:</p>
27722  * <pre>
27723 Value   Description
27724 ------  -------------------
27725  'n'     north
27726  's'     south
27727  'e'     east
27728  'w'     west
27729  'nw'    northwest
27730  'sw'    southwest
27731  'se'    southeast
27732  'ne'    northeast
27733  'hd'    horizontal drag
27734  'all'   all
27735 </pre>
27736  * <p>Here's an example showing the creation of a typical Resizable:</p>
27737  * <pre><code>
27738 var resizer = new Roo.Resizable("element-id", {
27739     handles: 'all',
27740     minWidth: 200,
27741     minHeight: 100,
27742     maxWidth: 500,
27743     maxHeight: 400,
27744     pinned: true
27745 });
27746 resizer.on("resize", myHandler);
27747 </code></pre>
27748  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
27749  * resizer.east.setDisplayed(false);</p>
27750  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
27751  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
27752  * resize operation's new size (defaults to [0, 0])
27753  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
27754  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
27755  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
27756  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
27757  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
27758  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
27759  * @cfg {Number} width The width of the element in pixels (defaults to null)
27760  * @cfg {Number} height The height of the element in pixels (defaults to null)
27761  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
27762  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
27763  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
27764  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
27765  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
27766  * in favor of the handles config option (defaults to false)
27767  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
27768  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
27769  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
27770  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
27771  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
27772  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
27773  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
27774  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
27775  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
27776  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
27777  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
27778  * @constructor
27779  * Create a new resizable component
27780  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
27781  * @param {Object} config configuration options
27782   */
27783 Roo.Resizable = function(el, config)
27784 {
27785     this.el = Roo.get(el);
27786
27787     if(config && config.wrap){
27788         config.resizeChild = this.el;
27789         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
27790         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
27791         this.el.setStyle("overflow", "hidden");
27792         this.el.setPositioning(config.resizeChild.getPositioning());
27793         config.resizeChild.clearPositioning();
27794         if(!config.width || !config.height){
27795             var csize = config.resizeChild.getSize();
27796             this.el.setSize(csize.width, csize.height);
27797         }
27798         if(config.pinned && !config.adjustments){
27799             config.adjustments = "auto";
27800         }
27801     }
27802
27803     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
27804     this.proxy.unselectable();
27805     this.proxy.enableDisplayMode('block');
27806
27807     Roo.apply(this, config);
27808
27809     if(this.pinned){
27810         this.disableTrackOver = true;
27811         this.el.addClass("x-resizable-pinned");
27812     }
27813     // if the element isn't positioned, make it relative
27814     var position = this.el.getStyle("position");
27815     if(position != "absolute" && position != "fixed"){
27816         this.el.setStyle("position", "relative");
27817     }
27818     if(!this.handles){ // no handles passed, must be legacy style
27819         this.handles = 's,e,se';
27820         if(this.multiDirectional){
27821             this.handles += ',n,w';
27822         }
27823     }
27824     if(this.handles == "all"){
27825         this.handles = "n s e w ne nw se sw";
27826     }
27827     var hs = this.handles.split(/\s*?[,;]\s*?| /);
27828     var ps = Roo.Resizable.positions;
27829     for(var i = 0, len = hs.length; i < len; i++){
27830         if(hs[i] && ps[hs[i]]){
27831             var pos = ps[hs[i]];
27832             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
27833         }
27834     }
27835     // legacy
27836     this.corner = this.southeast;
27837     
27838     // updateBox = the box can move..
27839     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
27840         this.updateBox = true;
27841     }
27842
27843     this.activeHandle = null;
27844
27845     if(this.resizeChild){
27846         if(typeof this.resizeChild == "boolean"){
27847             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
27848         }else{
27849             this.resizeChild = Roo.get(this.resizeChild, true);
27850         }
27851     }
27852     
27853     if(this.adjustments == "auto"){
27854         var rc = this.resizeChild;
27855         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
27856         if(rc && (hw || hn)){
27857             rc.position("relative");
27858             rc.setLeft(hw ? hw.el.getWidth() : 0);
27859             rc.setTop(hn ? hn.el.getHeight() : 0);
27860         }
27861         this.adjustments = [
27862             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
27863             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
27864         ];
27865     }
27866
27867     if(this.draggable){
27868         this.dd = this.dynamic ?
27869             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
27870         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
27871     }
27872
27873     // public events
27874     this.addEvents({
27875         /**
27876          * @event beforeresize
27877          * Fired before resize is allowed. Set enabled to false to cancel resize.
27878          * @param {Roo.Resizable} this
27879          * @param {Roo.EventObject} e The mousedown event
27880          */
27881         "beforeresize" : true,
27882         /**
27883          * @event resize
27884          * Fired after a resize.
27885          * @param {Roo.Resizable} this
27886          * @param {Number} width The new width
27887          * @param {Number} height The new height
27888          * @param {Roo.EventObject} e The mouseup event
27889          */
27890         "resize" : true
27891     });
27892
27893     if(this.width !== null && this.height !== null){
27894         this.resizeTo(this.width, this.height);
27895     }else{
27896         this.updateChildSize();
27897     }
27898     if(Roo.isIE){
27899         this.el.dom.style.zoom = 1;
27900     }
27901     Roo.Resizable.superclass.constructor.call(this);
27902 };
27903
27904 Roo.extend(Roo.Resizable, Roo.util.Observable, {
27905         resizeChild : false,
27906         adjustments : [0, 0],
27907         minWidth : 5,
27908         minHeight : 5,
27909         maxWidth : 10000,
27910         maxHeight : 10000,
27911         enabled : true,
27912         animate : false,
27913         duration : .35,
27914         dynamic : false,
27915         handles : false,
27916         multiDirectional : false,
27917         disableTrackOver : false,
27918         easing : 'easeOutStrong',
27919         widthIncrement : 0,
27920         heightIncrement : 0,
27921         pinned : false,
27922         width : null,
27923         height : null,
27924         preserveRatio : false,
27925         transparent: false,
27926         minX: 0,
27927         minY: 0,
27928         draggable: false,
27929
27930         /**
27931          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
27932          */
27933         constrainTo: undefined,
27934         /**
27935          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
27936          */
27937         resizeRegion: undefined,
27938
27939
27940     /**
27941      * Perform a manual resize
27942      * @param {Number} width
27943      * @param {Number} height
27944      */
27945     resizeTo : function(width, height){
27946         this.el.setSize(width, height);
27947         this.updateChildSize();
27948         this.fireEvent("resize", this, width, height, null);
27949     },
27950
27951     // private
27952     startSizing : function(e, handle){
27953         this.fireEvent("beforeresize", this, e);
27954         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
27955
27956             if(!this.overlay){
27957                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
27958                 this.overlay.unselectable();
27959                 this.overlay.enableDisplayMode("block");
27960                 this.overlay.on("mousemove", this.onMouseMove, this);
27961                 this.overlay.on("mouseup", this.onMouseUp, this);
27962             }
27963             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
27964
27965             this.resizing = true;
27966             this.startBox = this.el.getBox();
27967             this.startPoint = e.getXY();
27968             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
27969                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
27970
27971             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
27972             this.overlay.show();
27973
27974             if(this.constrainTo) {
27975                 var ct = Roo.get(this.constrainTo);
27976                 this.resizeRegion = ct.getRegion().adjust(
27977                     ct.getFrameWidth('t'),
27978                     ct.getFrameWidth('l'),
27979                     -ct.getFrameWidth('b'),
27980                     -ct.getFrameWidth('r')
27981                 );
27982             }
27983
27984             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
27985             this.proxy.show();
27986             this.proxy.setBox(this.startBox);
27987             if(!this.dynamic){
27988                 this.proxy.setStyle('visibility', 'visible');
27989             }
27990         }
27991     },
27992
27993     // private
27994     onMouseDown : function(handle, e){
27995         if(this.enabled){
27996             e.stopEvent();
27997             this.activeHandle = handle;
27998             this.startSizing(e, handle);
27999         }
28000     },
28001
28002     // private
28003     onMouseUp : function(e){
28004         var size = this.resizeElement();
28005         this.resizing = false;
28006         this.handleOut();
28007         this.overlay.hide();
28008         this.proxy.hide();
28009         this.fireEvent("resize", this, size.width, size.height, e);
28010     },
28011
28012     // private
28013     updateChildSize : function(){
28014         if(this.resizeChild){
28015             var el = this.el;
28016             var child = this.resizeChild;
28017             var adj = this.adjustments;
28018             if(el.dom.offsetWidth){
28019                 var b = el.getSize(true);
28020                 child.setSize(b.width+adj[0], b.height+adj[1]);
28021             }
28022             // Second call here for IE
28023             // The first call enables instant resizing and
28024             // the second call corrects scroll bars if they
28025             // exist
28026             if(Roo.isIE){
28027                 setTimeout(function(){
28028                     if(el.dom.offsetWidth){
28029                         var b = el.getSize(true);
28030                         child.setSize(b.width+adj[0], b.height+adj[1]);
28031                     }
28032                 }, 10);
28033             }
28034         }
28035     },
28036
28037     // private
28038     snap : function(value, inc, min){
28039         if(!inc || !value) return value;
28040         var newValue = value;
28041         var m = value % inc;
28042         if(m > 0){
28043             if(m > (inc/2)){
28044                 newValue = value + (inc-m);
28045             }else{
28046                 newValue = value - m;
28047             }
28048         }
28049         return Math.max(min, newValue);
28050     },
28051
28052     // private
28053     resizeElement : function(){
28054         var box = this.proxy.getBox();
28055         if(this.updateBox){
28056             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
28057         }else{
28058             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
28059         }
28060         this.updateChildSize();
28061         if(!this.dynamic){
28062             this.proxy.hide();
28063         }
28064         return box;
28065     },
28066
28067     // private
28068     constrain : function(v, diff, m, mx){
28069         if(v - diff < m){
28070             diff = v - m;
28071         }else if(v - diff > mx){
28072             diff = mx - v;
28073         }
28074         return diff;
28075     },
28076
28077     // private
28078     onMouseMove : function(e){
28079         if(this.enabled){
28080             try{// try catch so if something goes wrong the user doesn't get hung
28081
28082             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
28083                 return;
28084             }
28085
28086             //var curXY = this.startPoint;
28087             var curSize = this.curSize || this.startBox;
28088             var x = this.startBox.x, y = this.startBox.y;
28089             var ox = x, oy = y;
28090             var w = curSize.width, h = curSize.height;
28091             var ow = w, oh = h;
28092             var mw = this.minWidth, mh = this.minHeight;
28093             var mxw = this.maxWidth, mxh = this.maxHeight;
28094             var wi = this.widthIncrement;
28095             var hi = this.heightIncrement;
28096
28097             var eventXY = e.getXY();
28098             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
28099             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
28100
28101             var pos = this.activeHandle.position;
28102
28103             switch(pos){
28104                 case "east":
28105                     w += diffX;
28106                     w = Math.min(Math.max(mw, w), mxw);
28107                     break;
28108              
28109                 case "south":
28110                     h += diffY;
28111                     h = Math.min(Math.max(mh, h), mxh);
28112                     break;
28113                 case "southeast":
28114                     w += diffX;
28115                     h += diffY;
28116                     w = Math.min(Math.max(mw, w), mxw);
28117                     h = Math.min(Math.max(mh, h), mxh);
28118                     break;
28119                 case "north":
28120                     diffY = this.constrain(h, diffY, mh, mxh);
28121                     y += diffY;
28122                     h -= diffY;
28123                     break;
28124                 case "hdrag":
28125                     
28126                     if (wi) {
28127                         var adiffX = Math.abs(diffX);
28128                         var sub = (adiffX % wi); // how much 
28129                         if (sub > (wi/2)) { // far enough to snap
28130                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
28131                         } else {
28132                             // remove difference.. 
28133                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
28134                         }
28135                     }
28136                     x += diffX;
28137                     x = Math.max(this.minX, x);
28138                     break;
28139                 case "west":
28140                     diffX = this.constrain(w, diffX, mw, mxw);
28141                     x += diffX;
28142                     w -= diffX;
28143                     break;
28144                 case "northeast":
28145                     w += diffX;
28146                     w = Math.min(Math.max(mw, w), mxw);
28147                     diffY = this.constrain(h, diffY, mh, mxh);
28148                     y += diffY;
28149                     h -= diffY;
28150                     break;
28151                 case "northwest":
28152                     diffX = this.constrain(w, diffX, mw, mxw);
28153                     diffY = this.constrain(h, diffY, mh, mxh);
28154                     y += diffY;
28155                     h -= diffY;
28156                     x += diffX;
28157                     w -= diffX;
28158                     break;
28159                case "southwest":
28160                     diffX = this.constrain(w, diffX, mw, mxw);
28161                     h += diffY;
28162                     h = Math.min(Math.max(mh, h), mxh);
28163                     x += diffX;
28164                     w -= diffX;
28165                     break;
28166             }
28167
28168             var sw = this.snap(w, wi, mw);
28169             var sh = this.snap(h, hi, mh);
28170             if(sw != w || sh != h){
28171                 switch(pos){
28172                     case "northeast":
28173                         y -= sh - h;
28174                     break;
28175                     case "north":
28176                         y -= sh - h;
28177                         break;
28178                     case "southwest":
28179                         x -= sw - w;
28180                     break;
28181                     case "west":
28182                         x -= sw - w;
28183                         break;
28184                     case "northwest":
28185                         x -= sw - w;
28186                         y -= sh - h;
28187                     break;
28188                 }
28189                 w = sw;
28190                 h = sh;
28191             }
28192
28193             if(this.preserveRatio){
28194                 switch(pos){
28195                     case "southeast":
28196                     case "east":
28197                         h = oh * (w/ow);
28198                         h = Math.min(Math.max(mh, h), mxh);
28199                         w = ow * (h/oh);
28200                        break;
28201                     case "south":
28202                         w = ow * (h/oh);
28203                         w = Math.min(Math.max(mw, w), mxw);
28204                         h = oh * (w/ow);
28205                         break;
28206                     case "northeast":
28207                         w = ow * (h/oh);
28208                         w = Math.min(Math.max(mw, w), mxw);
28209                         h = oh * (w/ow);
28210                     break;
28211                     case "north":
28212                         var tw = w;
28213                         w = ow * (h/oh);
28214                         w = Math.min(Math.max(mw, w), mxw);
28215                         h = oh * (w/ow);
28216                         x += (tw - w) / 2;
28217                         break;
28218                     case "southwest":
28219                         h = oh * (w/ow);
28220                         h = Math.min(Math.max(mh, h), mxh);
28221                         var tw = w;
28222                         w = ow * (h/oh);
28223                         x += tw - w;
28224                         break;
28225                     case "west":
28226                         var th = h;
28227                         h = oh * (w/ow);
28228                         h = Math.min(Math.max(mh, h), mxh);
28229                         y += (th - h) / 2;
28230                         var tw = w;
28231                         w = ow * (h/oh);
28232                         x += tw - w;
28233                        break;
28234                     case "northwest":
28235                         var tw = w;
28236                         var th = h;
28237                         h = oh * (w/ow);
28238                         h = Math.min(Math.max(mh, h), mxh);
28239                         w = ow * (h/oh);
28240                         y += th - h;
28241                         x += tw - w;
28242                        break;
28243
28244                 }
28245             }
28246             if (pos == 'hdrag') {
28247                 w = ow;
28248             }
28249             this.proxy.setBounds(x, y, w, h);
28250             if(this.dynamic){
28251                 this.resizeElement();
28252             }
28253             }catch(e){}
28254         }
28255     },
28256
28257     // private
28258     handleOver : function(){
28259         if(this.enabled){
28260             this.el.addClass("x-resizable-over");
28261         }
28262     },
28263
28264     // private
28265     handleOut : function(){
28266         if(!this.resizing){
28267             this.el.removeClass("x-resizable-over");
28268         }
28269     },
28270
28271     /**
28272      * Returns the element this component is bound to.
28273      * @return {Roo.Element}
28274      */
28275     getEl : function(){
28276         return this.el;
28277     },
28278
28279     /**
28280      * Returns the resizeChild element (or null).
28281      * @return {Roo.Element}
28282      */
28283     getResizeChild : function(){
28284         return this.resizeChild;
28285     },
28286
28287     /**
28288      * Destroys this resizable. If the element was wrapped and
28289      * removeEl is not true then the element remains.
28290      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
28291      */
28292     destroy : function(removeEl){
28293         this.proxy.remove();
28294         if(this.overlay){
28295             this.overlay.removeAllListeners();
28296             this.overlay.remove();
28297         }
28298         var ps = Roo.Resizable.positions;
28299         for(var k in ps){
28300             if(typeof ps[k] != "function" && this[ps[k]]){
28301                 var h = this[ps[k]];
28302                 h.el.removeAllListeners();
28303                 h.el.remove();
28304             }
28305         }
28306         if(removeEl){
28307             this.el.update("");
28308             this.el.remove();
28309         }
28310     }
28311 });
28312
28313 // private
28314 // hash to map config positions to true positions
28315 Roo.Resizable.positions = {
28316     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
28317     hd: "hdrag"
28318 };
28319
28320 // private
28321 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
28322     if(!this.tpl){
28323         // only initialize the template if resizable is used
28324         var tpl = Roo.DomHelper.createTemplate(
28325             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
28326         );
28327         tpl.compile();
28328         Roo.Resizable.Handle.prototype.tpl = tpl;
28329     }
28330     this.position = pos;
28331     this.rz = rz;
28332     // show north drag fro topdra
28333     var handlepos = pos == 'hdrag' ? 'north' : pos;
28334     
28335     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
28336     if (pos == 'hdrag') {
28337         this.el.setStyle('cursor', 'pointer');
28338     }
28339     this.el.unselectable();
28340     if(transparent){
28341         this.el.setOpacity(0);
28342     }
28343     this.el.on("mousedown", this.onMouseDown, this);
28344     if(!disableTrackOver){
28345         this.el.on("mouseover", this.onMouseOver, this);
28346         this.el.on("mouseout", this.onMouseOut, this);
28347     }
28348 };
28349
28350 // private
28351 Roo.Resizable.Handle.prototype = {
28352     afterResize : function(rz){
28353         // do nothing
28354     },
28355     // private
28356     onMouseDown : function(e){
28357         this.rz.onMouseDown(this, e);
28358     },
28359     // private
28360     onMouseOver : function(e){
28361         this.rz.handleOver(this, e);
28362     },
28363     // private
28364     onMouseOut : function(e){
28365         this.rz.handleOut(this, e);
28366     }
28367 };/*
28368  * Based on:
28369  * Ext JS Library 1.1.1
28370  * Copyright(c) 2006-2007, Ext JS, LLC.
28371  *
28372  * Originally Released Under LGPL - original licence link has changed is not relivant.
28373  *
28374  * Fork - LGPL
28375  * <script type="text/javascript">
28376  */
28377
28378 /**
28379  * @class Roo.Editor
28380  * @extends Roo.Component
28381  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
28382  * @constructor
28383  * Create a new Editor
28384  * @param {Roo.form.Field} field The Field object (or descendant)
28385  * @param {Object} config The config object
28386  */
28387 Roo.Editor = function(field, config){
28388     Roo.Editor.superclass.constructor.call(this, config);
28389     this.field = field;
28390     this.addEvents({
28391         /**
28392              * @event beforestartedit
28393              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
28394              * false from the handler of this event.
28395              * @param {Editor} this
28396              * @param {Roo.Element} boundEl The underlying element bound to this editor
28397              * @param {Mixed} value The field value being set
28398              */
28399         "beforestartedit" : true,
28400         /**
28401              * @event startedit
28402              * Fires when this editor is displayed
28403              * @param {Roo.Element} boundEl The underlying element bound to this editor
28404              * @param {Mixed} value The starting field value
28405              */
28406         "startedit" : true,
28407         /**
28408              * @event beforecomplete
28409              * Fires after a change has been made to the field, but before the change is reflected in the underlying
28410              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
28411              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
28412              * event will not fire since no edit actually occurred.
28413              * @param {Editor} this
28414              * @param {Mixed} value The current field value
28415              * @param {Mixed} startValue The original field value
28416              */
28417         "beforecomplete" : true,
28418         /**
28419              * @event complete
28420              * Fires after editing is complete and any changed value has been written to the underlying field.
28421              * @param {Editor} this
28422              * @param {Mixed} value The current field value
28423              * @param {Mixed} startValue The original field value
28424              */
28425         "complete" : true,
28426         /**
28427          * @event specialkey
28428          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
28429          * {@link Roo.EventObject#getKey} to determine which key was pressed.
28430          * @param {Roo.form.Field} this
28431          * @param {Roo.EventObject} e The event object
28432          */
28433         "specialkey" : true
28434     });
28435 };
28436
28437 Roo.extend(Roo.Editor, Roo.Component, {
28438     /**
28439      * @cfg {Boolean/String} autosize
28440      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
28441      * or "height" to adopt the height only (defaults to false)
28442      */
28443     /**
28444      * @cfg {Boolean} revertInvalid
28445      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
28446      * validation fails (defaults to true)
28447      */
28448     /**
28449      * @cfg {Boolean} ignoreNoChange
28450      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
28451      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
28452      * will never be ignored.
28453      */
28454     /**
28455      * @cfg {Boolean} hideEl
28456      * False to keep the bound element visible while the editor is displayed (defaults to true)
28457      */
28458     /**
28459      * @cfg {Mixed} value
28460      * The data value of the underlying field (defaults to "")
28461      */
28462     value : "",
28463     /**
28464      * @cfg {String} alignment
28465      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
28466      */
28467     alignment: "c-c?",
28468     /**
28469      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
28470      * for bottom-right shadow (defaults to "frame")
28471      */
28472     shadow : "frame",
28473     /**
28474      * @cfg {Boolean} constrain True to constrain the editor to the viewport
28475      */
28476     constrain : false,
28477     /**
28478      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
28479      */
28480     completeOnEnter : false,
28481     /**
28482      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
28483      */
28484     cancelOnEsc : false,
28485     /**
28486      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
28487      */
28488     updateEl : false,
28489
28490     // private
28491     onRender : function(ct, position){
28492         this.el = new Roo.Layer({
28493             shadow: this.shadow,
28494             cls: "x-editor",
28495             parentEl : ct,
28496             shim : this.shim,
28497             shadowOffset:4,
28498             id: this.id,
28499             constrain: this.constrain
28500         });
28501         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
28502         if(this.field.msgTarget != 'title'){
28503             this.field.msgTarget = 'qtip';
28504         }
28505         this.field.render(this.el);
28506         if(Roo.isGecko){
28507             this.field.el.dom.setAttribute('autocomplete', 'off');
28508         }
28509         this.field.on("specialkey", this.onSpecialKey, this);
28510         if(this.swallowKeys){
28511             this.field.el.swallowEvent(['keydown','keypress']);
28512         }
28513         this.field.show();
28514         this.field.on("blur", this.onBlur, this);
28515         if(this.field.grow){
28516             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
28517         }
28518     },
28519
28520     onSpecialKey : function(field, e)
28521     {
28522         //Roo.log('editor onSpecialKey');
28523         if(this.completeOnEnter && e.getKey() == e.ENTER){
28524             e.stopEvent();
28525             this.completeEdit();
28526             return;
28527         }
28528         // do not fire special key otherwise it might hide close the editor...
28529         if(e.getKey() == e.ENTER){    
28530             return;
28531         }
28532         if(this.cancelOnEsc && e.getKey() == e.ESC){
28533             this.cancelEdit();
28534             return;
28535         } 
28536         this.fireEvent('specialkey', field, e);
28537     
28538     },
28539
28540     /**
28541      * Starts the editing process and shows the editor.
28542      * @param {String/HTMLElement/Element} el The element to edit
28543      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
28544       * to the innerHTML of el.
28545      */
28546     startEdit : function(el, value){
28547         if(this.editing){
28548             this.completeEdit();
28549         }
28550         this.boundEl = Roo.get(el);
28551         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
28552         if(!this.rendered){
28553             this.render(this.parentEl || document.body);
28554         }
28555         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
28556             return;
28557         }
28558         this.startValue = v;
28559         this.field.setValue(v);
28560         if(this.autoSize){
28561             var sz = this.boundEl.getSize();
28562             switch(this.autoSize){
28563                 case "width":
28564                 this.setSize(sz.width,  "");
28565                 break;
28566                 case "height":
28567                 this.setSize("",  sz.height);
28568                 break;
28569                 default:
28570                 this.setSize(sz.width,  sz.height);
28571             }
28572         }
28573         this.el.alignTo(this.boundEl, this.alignment);
28574         this.editing = true;
28575         if(Roo.QuickTips){
28576             Roo.QuickTips.disable();
28577         }
28578         this.show();
28579     },
28580
28581     /**
28582      * Sets the height and width of this editor.
28583      * @param {Number} width The new width
28584      * @param {Number} height The new height
28585      */
28586     setSize : function(w, h){
28587         this.field.setSize(w, h);
28588         if(this.el){
28589             this.el.sync();
28590         }
28591     },
28592
28593     /**
28594      * Realigns the editor to the bound field based on the current alignment config value.
28595      */
28596     realign : function(){
28597         this.el.alignTo(this.boundEl, this.alignment);
28598     },
28599
28600     /**
28601      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
28602      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
28603      */
28604     completeEdit : function(remainVisible){
28605         if(!this.editing){
28606             return;
28607         }
28608         var v = this.getValue();
28609         if(this.revertInvalid !== false && !this.field.isValid()){
28610             v = this.startValue;
28611             this.cancelEdit(true);
28612         }
28613         if(String(v) === String(this.startValue) && this.ignoreNoChange){
28614             this.editing = false;
28615             this.hide();
28616             return;
28617         }
28618         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
28619             this.editing = false;
28620             if(this.updateEl && this.boundEl){
28621                 this.boundEl.update(v);
28622             }
28623             if(remainVisible !== true){
28624                 this.hide();
28625             }
28626             this.fireEvent("complete", this, v, this.startValue);
28627         }
28628     },
28629
28630     // private
28631     onShow : function(){
28632         this.el.show();
28633         if(this.hideEl !== false){
28634             this.boundEl.hide();
28635         }
28636         this.field.show();
28637         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
28638             this.fixIEFocus = true;
28639             this.deferredFocus.defer(50, this);
28640         }else{
28641             this.field.focus();
28642         }
28643         this.fireEvent("startedit", this.boundEl, this.startValue);
28644     },
28645
28646     deferredFocus : function(){
28647         if(this.editing){
28648             this.field.focus();
28649         }
28650     },
28651
28652     /**
28653      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
28654      * reverted to the original starting value.
28655      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
28656      * cancel (defaults to false)
28657      */
28658     cancelEdit : function(remainVisible){
28659         if(this.editing){
28660             this.setValue(this.startValue);
28661             if(remainVisible !== true){
28662                 this.hide();
28663             }
28664         }
28665     },
28666
28667     // private
28668     onBlur : function(){
28669         if(this.allowBlur !== true && this.editing){
28670             this.completeEdit();
28671         }
28672     },
28673
28674     // private
28675     onHide : function(){
28676         if(this.editing){
28677             this.completeEdit();
28678             return;
28679         }
28680         this.field.blur();
28681         if(this.field.collapse){
28682             this.field.collapse();
28683         }
28684         this.el.hide();
28685         if(this.hideEl !== false){
28686             this.boundEl.show();
28687         }
28688         if(Roo.QuickTips){
28689             Roo.QuickTips.enable();
28690         }
28691     },
28692
28693     /**
28694      * Sets the data value of the editor
28695      * @param {Mixed} value Any valid value supported by the underlying field
28696      */
28697     setValue : function(v){
28698         this.field.setValue(v);
28699     },
28700
28701     /**
28702      * Gets the data value of the editor
28703      * @return {Mixed} The data value
28704      */
28705     getValue : function(){
28706         return this.field.getValue();
28707     }
28708 });/*
28709  * Based on:
28710  * Ext JS Library 1.1.1
28711  * Copyright(c) 2006-2007, Ext JS, LLC.
28712  *
28713  * Originally Released Under LGPL - original licence link has changed is not relivant.
28714  *
28715  * Fork - LGPL
28716  * <script type="text/javascript">
28717  */
28718  
28719 /**
28720  * @class Roo.BasicDialog
28721  * @extends Roo.util.Observable
28722  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
28723  * <pre><code>
28724 var dlg = new Roo.BasicDialog("my-dlg", {
28725     height: 200,
28726     width: 300,
28727     minHeight: 100,
28728     minWidth: 150,
28729     modal: true,
28730     proxyDrag: true,
28731     shadow: true
28732 });
28733 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
28734 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
28735 dlg.addButton('Cancel', dlg.hide, dlg);
28736 dlg.show();
28737 </code></pre>
28738   <b>A Dialog should always be a direct child of the body element.</b>
28739  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
28740  * @cfg {String} title Default text to display in the title bar (defaults to null)
28741  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
28742  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
28743  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
28744  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
28745  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
28746  * (defaults to null with no animation)
28747  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
28748  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
28749  * property for valid values (defaults to 'all')
28750  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
28751  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
28752  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
28753  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
28754  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
28755  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
28756  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
28757  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
28758  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
28759  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
28760  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
28761  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
28762  * draggable = true (defaults to false)
28763  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
28764  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
28765  * shadow (defaults to false)
28766  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
28767  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
28768  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
28769  * @cfg {Array} buttons Array of buttons
28770  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
28771  * @constructor
28772  * Create a new BasicDialog.
28773  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
28774  * @param {Object} config Configuration options
28775  */
28776 Roo.BasicDialog = function(el, config){
28777     this.el = Roo.get(el);
28778     var dh = Roo.DomHelper;
28779     if(!this.el && config && config.autoCreate){
28780         if(typeof config.autoCreate == "object"){
28781             if(!config.autoCreate.id){
28782                 config.autoCreate.id = el;
28783             }
28784             this.el = dh.append(document.body,
28785                         config.autoCreate, true);
28786         }else{
28787             this.el = dh.append(document.body,
28788                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
28789         }
28790     }
28791     el = this.el;
28792     el.setDisplayed(true);
28793     el.hide = this.hideAction;
28794     this.id = el.id;
28795     el.addClass("x-dlg");
28796
28797     Roo.apply(this, config);
28798
28799     this.proxy = el.createProxy("x-dlg-proxy");
28800     this.proxy.hide = this.hideAction;
28801     this.proxy.setOpacity(.5);
28802     this.proxy.hide();
28803
28804     if(config.width){
28805         el.setWidth(config.width);
28806     }
28807     if(config.height){
28808         el.setHeight(config.height);
28809     }
28810     this.size = el.getSize();
28811     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
28812         this.xy = [config.x,config.y];
28813     }else{
28814         this.xy = el.getCenterXY(true);
28815     }
28816     /** The header element @type Roo.Element */
28817     this.header = el.child("> .x-dlg-hd");
28818     /** The body element @type Roo.Element */
28819     this.body = el.child("> .x-dlg-bd");
28820     /** The footer element @type Roo.Element */
28821     this.footer = el.child("> .x-dlg-ft");
28822
28823     if(!this.header){
28824         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
28825     }
28826     if(!this.body){
28827         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
28828     }
28829
28830     this.header.unselectable();
28831     if(this.title){
28832         this.header.update(this.title);
28833     }
28834     // this element allows the dialog to be focused for keyboard event
28835     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
28836     this.focusEl.swallowEvent("click", true);
28837
28838     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
28839
28840     // wrap the body and footer for special rendering
28841     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
28842     if(this.footer){
28843         this.bwrap.dom.appendChild(this.footer.dom);
28844     }
28845
28846     this.bg = this.el.createChild({
28847         tag: "div", cls:"x-dlg-bg",
28848         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
28849     });
28850     this.centerBg = this.bg.child("div.x-dlg-bg-center");
28851
28852
28853     if(this.autoScroll !== false && !this.autoTabs){
28854         this.body.setStyle("overflow", "auto");
28855     }
28856
28857     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
28858
28859     if(this.closable !== false){
28860         this.el.addClass("x-dlg-closable");
28861         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
28862         this.close.on("click", this.closeClick, this);
28863         this.close.addClassOnOver("x-dlg-close-over");
28864     }
28865     if(this.collapsible !== false){
28866         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
28867         this.collapseBtn.on("click", this.collapseClick, this);
28868         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
28869         this.header.on("dblclick", this.collapseClick, this);
28870     }
28871     if(this.resizable !== false){
28872         this.el.addClass("x-dlg-resizable");
28873         this.resizer = new Roo.Resizable(el, {
28874             minWidth: this.minWidth || 80,
28875             minHeight:this.minHeight || 80,
28876             handles: this.resizeHandles || "all",
28877             pinned: true
28878         });
28879         this.resizer.on("beforeresize", this.beforeResize, this);
28880         this.resizer.on("resize", this.onResize, this);
28881     }
28882     if(this.draggable !== false){
28883         el.addClass("x-dlg-draggable");
28884         if (!this.proxyDrag) {
28885             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
28886         }
28887         else {
28888             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
28889         }
28890         dd.setHandleElId(this.header.id);
28891         dd.endDrag = this.endMove.createDelegate(this);
28892         dd.startDrag = this.startMove.createDelegate(this);
28893         dd.onDrag = this.onDrag.createDelegate(this);
28894         dd.scroll = false;
28895         this.dd = dd;
28896     }
28897     if(this.modal){
28898         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
28899         this.mask.enableDisplayMode("block");
28900         this.mask.hide();
28901         this.el.addClass("x-dlg-modal");
28902     }
28903     if(this.shadow){
28904         this.shadow = new Roo.Shadow({
28905             mode : typeof this.shadow == "string" ? this.shadow : "sides",
28906             offset : this.shadowOffset
28907         });
28908     }else{
28909         this.shadowOffset = 0;
28910     }
28911     if(Roo.useShims && this.shim !== false){
28912         this.shim = this.el.createShim();
28913         this.shim.hide = this.hideAction;
28914         this.shim.hide();
28915     }else{
28916         this.shim = false;
28917     }
28918     if(this.autoTabs){
28919         this.initTabs();
28920     }
28921     if (this.buttons) { 
28922         var bts= this.buttons;
28923         this.buttons = [];
28924         Roo.each(bts, function(b) {
28925             this.addButton(b);
28926         }, this);
28927     }
28928     
28929     
28930     this.addEvents({
28931         /**
28932          * @event keydown
28933          * Fires when a key is pressed
28934          * @param {Roo.BasicDialog} this
28935          * @param {Roo.EventObject} e
28936          */
28937         "keydown" : true,
28938         /**
28939          * @event move
28940          * Fires when this dialog is moved by the user.
28941          * @param {Roo.BasicDialog} this
28942          * @param {Number} x The new page X
28943          * @param {Number} y The new page Y
28944          */
28945         "move" : true,
28946         /**
28947          * @event resize
28948          * Fires when this dialog is resized by the user.
28949          * @param {Roo.BasicDialog} this
28950          * @param {Number} width The new width
28951          * @param {Number} height The new height
28952          */
28953         "resize" : true,
28954         /**
28955          * @event beforehide
28956          * Fires before this dialog is hidden.
28957          * @param {Roo.BasicDialog} this
28958          */
28959         "beforehide" : true,
28960         /**
28961          * @event hide
28962          * Fires when this dialog is hidden.
28963          * @param {Roo.BasicDialog} this
28964          */
28965         "hide" : true,
28966         /**
28967          * @event beforeshow
28968          * Fires before this dialog is shown.
28969          * @param {Roo.BasicDialog} this
28970          */
28971         "beforeshow" : true,
28972         /**
28973          * @event show
28974          * Fires when this dialog is shown.
28975          * @param {Roo.BasicDialog} this
28976          */
28977         "show" : true
28978     });
28979     el.on("keydown", this.onKeyDown, this);
28980     el.on("mousedown", this.toFront, this);
28981     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
28982     this.el.hide();
28983     Roo.DialogManager.register(this);
28984     Roo.BasicDialog.superclass.constructor.call(this);
28985 };
28986
28987 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
28988     shadowOffset: Roo.isIE ? 6 : 5,
28989     minHeight: 80,
28990     minWidth: 200,
28991     minButtonWidth: 75,
28992     defaultButton: null,
28993     buttonAlign: "right",
28994     tabTag: 'div',
28995     firstShow: true,
28996
28997     /**
28998      * Sets the dialog title text
28999      * @param {String} text The title text to display
29000      * @return {Roo.BasicDialog} this
29001      */
29002     setTitle : function(text){
29003         this.header.update(text);
29004         return this;
29005     },
29006
29007     // private
29008     closeClick : function(){
29009         this.hide();
29010     },
29011
29012     // private
29013     collapseClick : function(){
29014         this[this.collapsed ? "expand" : "collapse"]();
29015     },
29016
29017     /**
29018      * Collapses the dialog to its minimized state (only the title bar is visible).
29019      * Equivalent to the user clicking the collapse dialog button.
29020      */
29021     collapse : function(){
29022         if(!this.collapsed){
29023             this.collapsed = true;
29024             this.el.addClass("x-dlg-collapsed");
29025             this.restoreHeight = this.el.getHeight();
29026             this.resizeTo(this.el.getWidth(), this.header.getHeight());
29027         }
29028     },
29029
29030     /**
29031      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
29032      * clicking the expand dialog button.
29033      */
29034     expand : function(){
29035         if(this.collapsed){
29036             this.collapsed = false;
29037             this.el.removeClass("x-dlg-collapsed");
29038             this.resizeTo(this.el.getWidth(), this.restoreHeight);
29039         }
29040     },
29041
29042     /**
29043      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
29044      * @return {Roo.TabPanel} The tabs component
29045      */
29046     initTabs : function(){
29047         var tabs = this.getTabs();
29048         while(tabs.getTab(0)){
29049             tabs.removeTab(0);
29050         }
29051         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
29052             var dom = el.dom;
29053             tabs.addTab(Roo.id(dom), dom.title);
29054             dom.title = "";
29055         });
29056         tabs.activate(0);
29057         return tabs;
29058     },
29059
29060     // private
29061     beforeResize : function(){
29062         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
29063     },
29064
29065     // private
29066     onResize : function(){
29067         this.refreshSize();
29068         this.syncBodyHeight();
29069         this.adjustAssets();
29070         this.focus();
29071         this.fireEvent("resize", this, this.size.width, this.size.height);
29072     },
29073
29074     // private
29075     onKeyDown : function(e){
29076         if(this.isVisible()){
29077             this.fireEvent("keydown", this, e);
29078         }
29079     },
29080
29081     /**
29082      * Resizes the dialog.
29083      * @param {Number} width
29084      * @param {Number} height
29085      * @return {Roo.BasicDialog} this
29086      */
29087     resizeTo : function(width, height){
29088         this.el.setSize(width, height);
29089         this.size = {width: width, height: height};
29090         this.syncBodyHeight();
29091         if(this.fixedcenter){
29092             this.center();
29093         }
29094         if(this.isVisible()){
29095             this.constrainXY();
29096             this.adjustAssets();
29097         }
29098         this.fireEvent("resize", this, width, height);
29099         return this;
29100     },
29101
29102
29103     /**
29104      * Resizes the dialog to fit the specified content size.
29105      * @param {Number} width
29106      * @param {Number} height
29107      * @return {Roo.BasicDialog} this
29108      */
29109     setContentSize : function(w, h){
29110         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
29111         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
29112         //if(!this.el.isBorderBox()){
29113             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
29114             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
29115         //}
29116         if(this.tabs){
29117             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
29118             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
29119         }
29120         this.resizeTo(w, h);
29121         return this;
29122     },
29123
29124     /**
29125      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
29126      * executed in response to a particular key being pressed while the dialog is active.
29127      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
29128      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
29129      * @param {Function} fn The function to call
29130      * @param {Object} scope (optional) The scope of the function
29131      * @return {Roo.BasicDialog} this
29132      */
29133     addKeyListener : function(key, fn, scope){
29134         var keyCode, shift, ctrl, alt;
29135         if(typeof key == "object" && !(key instanceof Array)){
29136             keyCode = key["key"];
29137             shift = key["shift"];
29138             ctrl = key["ctrl"];
29139             alt = key["alt"];
29140         }else{
29141             keyCode = key;
29142         }
29143         var handler = function(dlg, e){
29144             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
29145                 var k = e.getKey();
29146                 if(keyCode instanceof Array){
29147                     for(var i = 0, len = keyCode.length; i < len; i++){
29148                         if(keyCode[i] == k){
29149                           fn.call(scope || window, dlg, k, e);
29150                           return;
29151                         }
29152                     }
29153                 }else{
29154                     if(k == keyCode){
29155                         fn.call(scope || window, dlg, k, e);
29156                     }
29157                 }
29158             }
29159         };
29160         this.on("keydown", handler);
29161         return this;
29162     },
29163
29164     /**
29165      * Returns the TabPanel component (creates it if it doesn't exist).
29166      * Note: If you wish to simply check for the existence of tabs without creating them,
29167      * check for a null 'tabs' property.
29168      * @return {Roo.TabPanel} The tabs component
29169      */
29170     getTabs : function(){
29171         if(!this.tabs){
29172             this.el.addClass("x-dlg-auto-tabs");
29173             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
29174             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
29175         }
29176         return this.tabs;
29177     },
29178
29179     /**
29180      * Adds a button to the footer section of the dialog.
29181      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
29182      * object or a valid Roo.DomHelper element config
29183      * @param {Function} handler The function called when the button is clicked
29184      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
29185      * @return {Roo.Button} The new button
29186      */
29187     addButton : function(config, handler, scope){
29188         var dh = Roo.DomHelper;
29189         if(!this.footer){
29190             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
29191         }
29192         if(!this.btnContainer){
29193             var tb = this.footer.createChild({
29194
29195                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
29196                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
29197             }, null, true);
29198             this.btnContainer = tb.firstChild.firstChild.firstChild;
29199         }
29200         var bconfig = {
29201             handler: handler,
29202             scope: scope,
29203             minWidth: this.minButtonWidth,
29204             hideParent:true
29205         };
29206         if(typeof config == "string"){
29207             bconfig.text = config;
29208         }else{
29209             if(config.tag){
29210                 bconfig.dhconfig = config;
29211             }else{
29212                 Roo.apply(bconfig, config);
29213             }
29214         }
29215         var fc = false;
29216         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
29217             bconfig.position = Math.max(0, bconfig.position);
29218             fc = this.btnContainer.childNodes[bconfig.position];
29219         }
29220          
29221         var btn = new Roo.Button(
29222             fc ? 
29223                 this.btnContainer.insertBefore(document.createElement("td"),fc)
29224                 : this.btnContainer.appendChild(document.createElement("td")),
29225             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
29226             bconfig
29227         );
29228         this.syncBodyHeight();
29229         if(!this.buttons){
29230             /**
29231              * Array of all the buttons that have been added to this dialog via addButton
29232              * @type Array
29233              */
29234             this.buttons = [];
29235         }
29236         this.buttons.push(btn);
29237         return btn;
29238     },
29239
29240     /**
29241      * Sets the default button to be focused when the dialog is displayed.
29242      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
29243      * @return {Roo.BasicDialog} this
29244      */
29245     setDefaultButton : function(btn){
29246         this.defaultButton = btn;
29247         return this;
29248     },
29249
29250     // private
29251     getHeaderFooterHeight : function(safe){
29252         var height = 0;
29253         if(this.header){
29254            height += this.header.getHeight();
29255         }
29256         if(this.footer){
29257            var fm = this.footer.getMargins();
29258             height += (this.footer.getHeight()+fm.top+fm.bottom);
29259         }
29260         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
29261         height += this.centerBg.getPadding("tb");
29262         return height;
29263     },
29264
29265     // private
29266     syncBodyHeight : function(){
29267         var bd = this.body, cb = this.centerBg, bw = this.bwrap;
29268         var height = this.size.height - this.getHeaderFooterHeight(false);
29269         bd.setHeight(height-bd.getMargins("tb"));
29270         var hh = this.header.getHeight();
29271         var h = this.size.height-hh;
29272         cb.setHeight(h);
29273         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
29274         bw.setHeight(h-cb.getPadding("tb"));
29275         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
29276         bd.setWidth(bw.getWidth(true));
29277         if(this.tabs){
29278             this.tabs.syncHeight();
29279             if(Roo.isIE){
29280                 this.tabs.el.repaint();
29281             }
29282         }
29283     },
29284
29285     /**
29286      * Restores the previous state of the dialog if Roo.state is configured.
29287      * @return {Roo.BasicDialog} this
29288      */
29289     restoreState : function(){
29290         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
29291         if(box && box.width){
29292             this.xy = [box.x, box.y];
29293             this.resizeTo(box.width, box.height);
29294         }
29295         return this;
29296     },
29297
29298     // private
29299     beforeShow : function(){
29300         this.expand();
29301         if(this.fixedcenter){
29302             this.xy = this.el.getCenterXY(true);
29303         }
29304         if(this.modal){
29305             Roo.get(document.body).addClass("x-body-masked");
29306             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29307             this.mask.show();
29308         }
29309         this.constrainXY();
29310     },
29311
29312     // private
29313     animShow : function(){
29314         var b = Roo.get(this.animateTarget).getBox();
29315         this.proxy.setSize(b.width, b.height);
29316         this.proxy.setLocation(b.x, b.y);
29317         this.proxy.show();
29318         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
29319                     true, .35, this.showEl.createDelegate(this));
29320     },
29321
29322     /**
29323      * Shows the dialog.
29324      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
29325      * @return {Roo.BasicDialog} this
29326      */
29327     show : function(animateTarget){
29328         if (this.fireEvent("beforeshow", this) === false){
29329             return;
29330         }
29331         if(this.syncHeightBeforeShow){
29332             this.syncBodyHeight();
29333         }else if(this.firstShow){
29334             this.firstShow = false;
29335             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
29336         }
29337         this.animateTarget = animateTarget || this.animateTarget;
29338         if(!this.el.isVisible()){
29339             this.beforeShow();
29340             if(this.animateTarget && Roo.get(this.animateTarget)){
29341                 this.animShow();
29342             }else{
29343                 this.showEl();
29344             }
29345         }
29346         return this;
29347     },
29348
29349     // private
29350     showEl : function(){
29351         this.proxy.hide();
29352         this.el.setXY(this.xy);
29353         this.el.show();
29354         this.adjustAssets(true);
29355         this.toFront();
29356         this.focus();
29357         // IE peekaboo bug - fix found by Dave Fenwick
29358         if(Roo.isIE){
29359             this.el.repaint();
29360         }
29361         this.fireEvent("show", this);
29362     },
29363
29364     /**
29365      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
29366      * dialog itself will receive focus.
29367      */
29368     focus : function(){
29369         if(this.defaultButton){
29370             this.defaultButton.focus();
29371         }else{
29372             this.focusEl.focus();
29373         }
29374     },
29375
29376     // private
29377     constrainXY : function(){
29378         if(this.constraintoviewport !== false){
29379             if(!this.viewSize){
29380                 if(this.container){
29381                     var s = this.container.getSize();
29382                     this.viewSize = [s.width, s.height];
29383                 }else{
29384                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
29385                 }
29386             }
29387             var s = Roo.get(this.container||document).getScroll();
29388
29389             var x = this.xy[0], y = this.xy[1];
29390             var w = this.size.width, h = this.size.height;
29391             var vw = this.viewSize[0], vh = this.viewSize[1];
29392             // only move it if it needs it
29393             var moved = false;
29394             // first validate right/bottom
29395             if(x + w > vw+s.left){
29396                 x = vw - w;
29397                 moved = true;
29398             }
29399             if(y + h > vh+s.top){
29400                 y = vh - h;
29401                 moved = true;
29402             }
29403             // then make sure top/left isn't negative
29404             if(x < s.left){
29405                 x = s.left;
29406                 moved = true;
29407             }
29408             if(y < s.top){
29409                 y = s.top;
29410                 moved = true;
29411             }
29412             if(moved){
29413                 // cache xy
29414                 this.xy = [x, y];
29415                 if(this.isVisible()){
29416                     this.el.setLocation(x, y);
29417                     this.adjustAssets();
29418                 }
29419             }
29420         }
29421     },
29422
29423     // private
29424     onDrag : function(){
29425         if(!this.proxyDrag){
29426             this.xy = this.el.getXY();
29427             this.adjustAssets();
29428         }
29429     },
29430
29431     // private
29432     adjustAssets : function(doShow){
29433         var x = this.xy[0], y = this.xy[1];
29434         var w = this.size.width, h = this.size.height;
29435         if(doShow === true){
29436             if(this.shadow){
29437                 this.shadow.show(this.el);
29438             }
29439             if(this.shim){
29440                 this.shim.show();
29441             }
29442         }
29443         if(this.shadow && this.shadow.isVisible()){
29444             this.shadow.show(this.el);
29445         }
29446         if(this.shim && this.shim.isVisible()){
29447             this.shim.setBounds(x, y, w, h);
29448         }
29449     },
29450
29451     // private
29452     adjustViewport : function(w, h){
29453         if(!w || !h){
29454             w = Roo.lib.Dom.getViewWidth();
29455             h = Roo.lib.Dom.getViewHeight();
29456         }
29457         // cache the size
29458         this.viewSize = [w, h];
29459         if(this.modal && this.mask.isVisible()){
29460             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
29461             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29462         }
29463         if(this.isVisible()){
29464             this.constrainXY();
29465         }
29466     },
29467
29468     /**
29469      * Destroys this dialog and all its supporting elements (including any tabs, shim,
29470      * shadow, proxy, mask, etc.)  Also removes all event listeners.
29471      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
29472      */
29473     destroy : function(removeEl){
29474         if(this.isVisible()){
29475             this.animateTarget = null;
29476             this.hide();
29477         }
29478         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
29479         if(this.tabs){
29480             this.tabs.destroy(removeEl);
29481         }
29482         Roo.destroy(
29483              this.shim,
29484              this.proxy,
29485              this.resizer,
29486              this.close,
29487              this.mask
29488         );
29489         if(this.dd){
29490             this.dd.unreg();
29491         }
29492         if(this.buttons){
29493            for(var i = 0, len = this.buttons.length; i < len; i++){
29494                this.buttons[i].destroy();
29495            }
29496         }
29497         this.el.removeAllListeners();
29498         if(removeEl === true){
29499             this.el.update("");
29500             this.el.remove();
29501         }
29502         Roo.DialogManager.unregister(this);
29503     },
29504
29505     // private
29506     startMove : function(){
29507         if(this.proxyDrag){
29508             this.proxy.show();
29509         }
29510         if(this.constraintoviewport !== false){
29511             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
29512         }
29513     },
29514
29515     // private
29516     endMove : function(){
29517         if(!this.proxyDrag){
29518             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
29519         }else{
29520             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
29521             this.proxy.hide();
29522         }
29523         this.refreshSize();
29524         this.adjustAssets();
29525         this.focus();
29526         this.fireEvent("move", this, this.xy[0], this.xy[1]);
29527     },
29528
29529     /**
29530      * Brings this dialog to the front of any other visible dialogs
29531      * @return {Roo.BasicDialog} this
29532      */
29533     toFront : function(){
29534         Roo.DialogManager.bringToFront(this);
29535         return this;
29536     },
29537
29538     /**
29539      * Sends this dialog to the back (under) of any other visible dialogs
29540      * @return {Roo.BasicDialog} this
29541      */
29542     toBack : function(){
29543         Roo.DialogManager.sendToBack(this);
29544         return this;
29545     },
29546
29547     /**
29548      * Centers this dialog in the viewport
29549      * @return {Roo.BasicDialog} this
29550      */
29551     center : function(){
29552         var xy = this.el.getCenterXY(true);
29553         this.moveTo(xy[0], xy[1]);
29554         return this;
29555     },
29556
29557     /**
29558      * Moves the dialog's top-left corner to the specified point
29559      * @param {Number} x
29560      * @param {Number} y
29561      * @return {Roo.BasicDialog} this
29562      */
29563     moveTo : function(x, y){
29564         this.xy = [x,y];
29565         if(this.isVisible()){
29566             this.el.setXY(this.xy);
29567             this.adjustAssets();
29568         }
29569         return this;
29570     },
29571
29572     /**
29573      * Aligns the dialog to the specified element
29574      * @param {String/HTMLElement/Roo.Element} element The element to align to.
29575      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
29576      * @param {Array} offsets (optional) Offset the positioning by [x, y]
29577      * @return {Roo.BasicDialog} this
29578      */
29579     alignTo : function(element, position, offsets){
29580         this.xy = this.el.getAlignToXY(element, position, offsets);
29581         if(this.isVisible()){
29582             this.el.setXY(this.xy);
29583             this.adjustAssets();
29584         }
29585         return this;
29586     },
29587
29588     /**
29589      * Anchors an element to another element and realigns it when the window is resized.
29590      * @param {String/HTMLElement/Roo.Element} element The element to align to.
29591      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
29592      * @param {Array} offsets (optional) Offset the positioning by [x, y]
29593      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
29594      * is a number, it is used as the buffer delay (defaults to 50ms).
29595      * @return {Roo.BasicDialog} this
29596      */
29597     anchorTo : function(el, alignment, offsets, monitorScroll){
29598         var action = function(){
29599             this.alignTo(el, alignment, offsets);
29600         };
29601         Roo.EventManager.onWindowResize(action, this);
29602         var tm = typeof monitorScroll;
29603         if(tm != 'undefined'){
29604             Roo.EventManager.on(window, 'scroll', action, this,
29605                 {buffer: tm == 'number' ? monitorScroll : 50});
29606         }
29607         action.call(this);
29608         return this;
29609     },
29610
29611     /**
29612      * Returns true if the dialog is visible
29613      * @return {Boolean}
29614      */
29615     isVisible : function(){
29616         return this.el.isVisible();
29617     },
29618
29619     // private
29620     animHide : function(callback){
29621         var b = Roo.get(this.animateTarget).getBox();
29622         this.proxy.show();
29623         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
29624         this.el.hide();
29625         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
29626                     this.hideEl.createDelegate(this, [callback]));
29627     },
29628
29629     /**
29630      * Hides the dialog.
29631      * @param {Function} callback (optional) Function to call when the dialog is hidden
29632      * @return {Roo.BasicDialog} this
29633      */
29634     hide : function(callback){
29635         if (this.fireEvent("beforehide", this) === false){
29636             return;
29637         }
29638         if(this.shadow){
29639             this.shadow.hide();
29640         }
29641         if(this.shim) {
29642           this.shim.hide();
29643         }
29644         // sometimes animateTarget seems to get set.. causing problems...
29645         // this just double checks..
29646         if(this.animateTarget && Roo.get(this.animateTarget)) {
29647            this.animHide(callback);
29648         }else{
29649             this.el.hide();
29650             this.hideEl(callback);
29651         }
29652         return this;
29653     },
29654
29655     // private
29656     hideEl : function(callback){
29657         this.proxy.hide();
29658         if(this.modal){
29659             this.mask.hide();
29660             Roo.get(document.body).removeClass("x-body-masked");
29661         }
29662         this.fireEvent("hide", this);
29663         if(typeof callback == "function"){
29664             callback();
29665         }
29666     },
29667
29668     // private
29669     hideAction : function(){
29670         this.setLeft("-10000px");
29671         this.setTop("-10000px");
29672         this.setStyle("visibility", "hidden");
29673     },
29674
29675     // private
29676     refreshSize : function(){
29677         this.size = this.el.getSize();
29678         this.xy = this.el.getXY();
29679         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
29680     },
29681
29682     // private
29683     // z-index is managed by the DialogManager and may be overwritten at any time
29684     setZIndex : function(index){
29685         if(this.modal){
29686             this.mask.setStyle("z-index", index);
29687         }
29688         if(this.shim){
29689             this.shim.setStyle("z-index", ++index);
29690         }
29691         if(this.shadow){
29692             this.shadow.setZIndex(++index);
29693         }
29694         this.el.setStyle("z-index", ++index);
29695         if(this.proxy){
29696             this.proxy.setStyle("z-index", ++index);
29697         }
29698         if(this.resizer){
29699             this.resizer.proxy.setStyle("z-index", ++index);
29700         }
29701
29702         this.lastZIndex = index;
29703     },
29704
29705     /**
29706      * Returns the element for this dialog
29707      * @return {Roo.Element} The underlying dialog Element
29708      */
29709     getEl : function(){
29710         return this.el;
29711     }
29712 });
29713
29714 /**
29715  * @class Roo.DialogManager
29716  * Provides global access to BasicDialogs that have been created and
29717  * support for z-indexing (layering) multiple open dialogs.
29718  */
29719 Roo.DialogManager = function(){
29720     var list = {};
29721     var accessList = [];
29722     var front = null;
29723
29724     // private
29725     var sortDialogs = function(d1, d2){
29726         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
29727     };
29728
29729     // private
29730     var orderDialogs = function(){
29731         accessList.sort(sortDialogs);
29732         var seed = Roo.DialogManager.zseed;
29733         for(var i = 0, len = accessList.length; i < len; i++){
29734             var dlg = accessList[i];
29735             if(dlg){
29736                 dlg.setZIndex(seed + (i*10));
29737             }
29738         }
29739     };
29740
29741     return {
29742         /**
29743          * The starting z-index for BasicDialogs (defaults to 9000)
29744          * @type Number The z-index value
29745          */
29746         zseed : 9000,
29747
29748         // private
29749         register : function(dlg){
29750             list[dlg.id] = dlg;
29751             accessList.push(dlg);
29752         },
29753
29754         // private
29755         unregister : function(dlg){
29756             delete list[dlg.id];
29757             var i=0;
29758             var len=0;
29759             if(!accessList.indexOf){
29760                 for(  i = 0, len = accessList.length; i < len; i++){
29761                     if(accessList[i] == dlg){
29762                         accessList.splice(i, 1);
29763                         return;
29764                     }
29765                 }
29766             }else{
29767                  i = accessList.indexOf(dlg);
29768                 if(i != -1){
29769                     accessList.splice(i, 1);
29770                 }
29771             }
29772         },
29773
29774         /**
29775          * Gets a registered dialog by id
29776          * @param {String/Object} id The id of the dialog or a dialog
29777          * @return {Roo.BasicDialog} this
29778          */
29779         get : function(id){
29780             return typeof id == "object" ? id : list[id];
29781         },
29782
29783         /**
29784          * Brings the specified dialog to the front
29785          * @param {String/Object} dlg The id of the dialog or a dialog
29786          * @return {Roo.BasicDialog} this
29787          */
29788         bringToFront : function(dlg){
29789             dlg = this.get(dlg);
29790             if(dlg != front){
29791                 front = dlg;
29792                 dlg._lastAccess = new Date().getTime();
29793                 orderDialogs();
29794             }
29795             return dlg;
29796         },
29797
29798         /**
29799          * Sends the specified dialog to the back
29800          * @param {String/Object} dlg The id of the dialog or a dialog
29801          * @return {Roo.BasicDialog} this
29802          */
29803         sendToBack : function(dlg){
29804             dlg = this.get(dlg);
29805             dlg._lastAccess = -(new Date().getTime());
29806             orderDialogs();
29807             return dlg;
29808         },
29809
29810         /**
29811          * Hides all dialogs
29812          */
29813         hideAll : function(){
29814             for(var id in list){
29815                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
29816                     list[id].hide();
29817                 }
29818             }
29819         }
29820     };
29821 }();
29822
29823 /**
29824  * @class Roo.LayoutDialog
29825  * @extends Roo.BasicDialog
29826  * Dialog which provides adjustments for working with a layout in a Dialog.
29827  * Add your necessary layout config options to the dialog's config.<br>
29828  * Example usage (including a nested layout):
29829  * <pre><code>
29830 if(!dialog){
29831     dialog = new Roo.LayoutDialog("download-dlg", {
29832         modal: true,
29833         width:600,
29834         height:450,
29835         shadow:true,
29836         minWidth:500,
29837         minHeight:350,
29838         autoTabs:true,
29839         proxyDrag:true,
29840         // layout config merges with the dialog config
29841         center:{
29842             tabPosition: "top",
29843             alwaysShowTabs: true
29844         }
29845     });
29846     dialog.addKeyListener(27, dialog.hide, dialog);
29847     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
29848     dialog.addButton("Build It!", this.getDownload, this);
29849
29850     // we can even add nested layouts
29851     var innerLayout = new Roo.BorderLayout("dl-inner", {
29852         east: {
29853             initialSize: 200,
29854             autoScroll:true,
29855             split:true
29856         },
29857         center: {
29858             autoScroll:true
29859         }
29860     });
29861     innerLayout.beginUpdate();
29862     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
29863     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
29864     innerLayout.endUpdate(true);
29865
29866     var layout = dialog.getLayout();
29867     layout.beginUpdate();
29868     layout.add("center", new Roo.ContentPanel("standard-panel",
29869                         {title: "Download the Source", fitToFrame:true}));
29870     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
29871                {title: "Build your own roo.js"}));
29872     layout.getRegion("center").showPanel(sp);
29873     layout.endUpdate();
29874 }
29875 </code></pre>
29876     * @constructor
29877     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
29878     * @param {Object} config configuration options
29879   */
29880 Roo.LayoutDialog = function(el, cfg){
29881     
29882     var config=  cfg;
29883     if (typeof(cfg) == 'undefined') {
29884         config = Roo.apply({}, el);
29885         // not sure why we use documentElement here.. - it should always be body.
29886         // IE7 borks horribly if we use documentElement.
29887         // webkit also does not like documentElement - it creates a body element...
29888         el = Roo.get( document.body || document.documentElement ).createChild();
29889         //config.autoCreate = true;
29890     }
29891     
29892     
29893     config.autoTabs = false;
29894     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
29895     this.body.setStyle({overflow:"hidden", position:"relative"});
29896     this.layout = new Roo.BorderLayout(this.body.dom, config);
29897     this.layout.monitorWindowResize = false;
29898     this.el.addClass("x-dlg-auto-layout");
29899     // fix case when center region overwrites center function
29900     this.center = Roo.BasicDialog.prototype.center;
29901     this.on("show", this.layout.layout, this.layout, true);
29902     if (config.items) {
29903         var xitems = config.items;
29904         delete config.items;
29905         Roo.each(xitems, this.addxtype, this);
29906     }
29907     
29908     
29909 };
29910 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
29911     /**
29912      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
29913      * @deprecated
29914      */
29915     endUpdate : function(){
29916         this.layout.endUpdate();
29917     },
29918
29919     /**
29920      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
29921      *  @deprecated
29922      */
29923     beginUpdate : function(){
29924         this.layout.beginUpdate();
29925     },
29926
29927     /**
29928      * Get the BorderLayout for this dialog
29929      * @return {Roo.BorderLayout}
29930      */
29931     getLayout : function(){
29932         return this.layout;
29933     },
29934
29935     showEl : function(){
29936         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
29937         if(Roo.isIE7){
29938             this.layout.layout();
29939         }
29940     },
29941
29942     // private
29943     // Use the syncHeightBeforeShow config option to control this automatically
29944     syncBodyHeight : function(){
29945         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
29946         if(this.layout){this.layout.layout();}
29947     },
29948     
29949       /**
29950      * Add an xtype element (actually adds to the layout.)
29951      * @return {Object} xdata xtype object data.
29952      */
29953     
29954     addxtype : function(c) {
29955         return this.layout.addxtype(c);
29956     }
29957 });/*
29958  * Based on:
29959  * Ext JS Library 1.1.1
29960  * Copyright(c) 2006-2007, Ext JS, LLC.
29961  *
29962  * Originally Released Under LGPL - original licence link has changed is not relivant.
29963  *
29964  * Fork - LGPL
29965  * <script type="text/javascript">
29966  */
29967  
29968 /**
29969  * @class Roo.MessageBox
29970  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
29971  * Example usage:
29972  *<pre><code>
29973 // Basic alert:
29974 Roo.Msg.alert('Status', 'Changes saved successfully.');
29975
29976 // Prompt for user data:
29977 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
29978     if (btn == 'ok'){
29979         // process text value...
29980     }
29981 });
29982
29983 // Show a dialog using config options:
29984 Roo.Msg.show({
29985    title:'Save Changes?',
29986    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
29987    buttons: Roo.Msg.YESNOCANCEL,
29988    fn: processResult,
29989    animEl: 'elId'
29990 });
29991 </code></pre>
29992  * @singleton
29993  */
29994 Roo.MessageBox = function(){
29995     var dlg, opt, mask, waitTimer;
29996     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
29997     var buttons, activeTextEl, bwidth;
29998
29999     // private
30000     var handleButton = function(button){
30001         dlg.hide();
30002         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
30003     };
30004
30005     // private
30006     var handleHide = function(){
30007         if(opt && opt.cls){
30008             dlg.el.removeClass(opt.cls);
30009         }
30010         if(waitTimer){
30011             Roo.TaskMgr.stop(waitTimer);
30012             waitTimer = null;
30013         }
30014     };
30015
30016     // private
30017     var updateButtons = function(b){
30018         var width = 0;
30019         if(!b){
30020             buttons["ok"].hide();
30021             buttons["cancel"].hide();
30022             buttons["yes"].hide();
30023             buttons["no"].hide();
30024             dlg.footer.dom.style.display = 'none';
30025             return width;
30026         }
30027         dlg.footer.dom.style.display = '';
30028         for(var k in buttons){
30029             if(typeof buttons[k] != "function"){
30030                 if(b[k]){
30031                     buttons[k].show();
30032                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
30033                     width += buttons[k].el.getWidth()+15;
30034                 }else{
30035                     buttons[k].hide();
30036                 }
30037             }
30038         }
30039         return width;
30040     };
30041
30042     // private
30043     var handleEsc = function(d, k, e){
30044         if(opt && opt.closable !== false){
30045             dlg.hide();
30046         }
30047         if(e){
30048             e.stopEvent();
30049         }
30050     };
30051
30052     return {
30053         /**
30054          * Returns a reference to the underlying {@link Roo.BasicDialog} element
30055          * @return {Roo.BasicDialog} The BasicDialog element
30056          */
30057         getDialog : function(){
30058            if(!dlg){
30059                 dlg = new Roo.BasicDialog("x-msg-box", {
30060                     autoCreate : true,
30061                     shadow: true,
30062                     draggable: true,
30063                     resizable:false,
30064                     constraintoviewport:false,
30065                     fixedcenter:true,
30066                     collapsible : false,
30067                     shim:true,
30068                     modal: true,
30069                     width:400, height:100,
30070                     buttonAlign:"center",
30071                     closeClick : function(){
30072                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
30073                             handleButton("no");
30074                         }else{
30075                             handleButton("cancel");
30076                         }
30077                     }
30078                 });
30079                 dlg.on("hide", handleHide);
30080                 mask = dlg.mask;
30081                 dlg.addKeyListener(27, handleEsc);
30082                 buttons = {};
30083                 var bt = this.buttonText;
30084                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
30085                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
30086                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
30087                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
30088                 bodyEl = dlg.body.createChild({
30089
30090                     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>'
30091                 });
30092                 msgEl = bodyEl.dom.firstChild;
30093                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
30094                 textboxEl.enableDisplayMode();
30095                 textboxEl.addKeyListener([10,13], function(){
30096                     if(dlg.isVisible() && opt && opt.buttons){
30097                         if(opt.buttons.ok){
30098                             handleButton("ok");
30099                         }else if(opt.buttons.yes){
30100                             handleButton("yes");
30101                         }
30102                     }
30103                 });
30104                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
30105                 textareaEl.enableDisplayMode();
30106                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
30107                 progressEl.enableDisplayMode();
30108                 var pf = progressEl.dom.firstChild;
30109                 if (pf) {
30110                     pp = Roo.get(pf.firstChild);
30111                     pp.setHeight(pf.offsetHeight);
30112                 }
30113                 
30114             }
30115             return dlg;
30116         },
30117
30118         /**
30119          * Updates the message box body text
30120          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
30121          * the XHTML-compliant non-breaking space character '&amp;#160;')
30122          * @return {Roo.MessageBox} This message box
30123          */
30124         updateText : function(text){
30125             if(!dlg.isVisible() && !opt.width){
30126                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
30127             }
30128             msgEl.innerHTML = text || '&#160;';
30129             var w = Math.max(Math.min(opt.width || msgEl.offsetWidth, this.maxWidth), 
30130                         Math.max(opt.minWidth || this.minWidth, bwidth));
30131             if(opt.prompt){
30132                 activeTextEl.setWidth(w);
30133             }
30134             if(dlg.isVisible()){
30135                 dlg.fixedcenter = false;
30136             }
30137             dlg.setContentSize(w, bodyEl.getHeight());
30138             if(dlg.isVisible()){
30139                 dlg.fixedcenter = true;
30140             }
30141             return this;
30142         },
30143
30144         /**
30145          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
30146          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
30147          * @param {Number} value Any number between 0 and 1 (e.g., .5)
30148          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
30149          * @return {Roo.MessageBox} This message box
30150          */
30151         updateProgress : function(value, text){
30152             if(text){
30153                 this.updateText(text);
30154             }
30155             if (pp) { // weird bug on my firefox - for some reason this is not defined
30156                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
30157             }
30158             return this;
30159         },        
30160
30161         /**
30162          * Returns true if the message box is currently displayed
30163          * @return {Boolean} True if the message box is visible, else false
30164          */
30165         isVisible : function(){
30166             return dlg && dlg.isVisible();  
30167         },
30168
30169         /**
30170          * Hides the message box if it is displayed
30171          */
30172         hide : function(){
30173             if(this.isVisible()){
30174                 dlg.hide();
30175             }  
30176         },
30177
30178         /**
30179          * Displays a new message box, or reinitializes an existing message box, based on the config options
30180          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
30181          * The following config object properties are supported:
30182          * <pre>
30183 Property    Type             Description
30184 ----------  ---------------  ------------------------------------------------------------------------------------
30185 animEl            String/Element   An id or Element from which the message box should animate as it opens and
30186                                    closes (defaults to undefined)
30187 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
30188                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
30189 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
30190                                    progress and wait dialogs will ignore this property and always hide the
30191                                    close button as they can only be closed programmatically.
30192 cls               String           A custom CSS class to apply to the message box element
30193 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
30194                                    displayed (defaults to 75)
30195 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
30196                                    function will be btn (the name of the button that was clicked, if applicable,
30197                                    e.g. "ok"), and text (the value of the active text field, if applicable).
30198                                    Progress and wait dialogs will ignore this option since they do not respond to
30199                                    user actions and can only be closed programmatically, so any required function
30200                                    should be called by the same code after it closes the dialog.
30201 icon              String           A CSS class that provides a background image to be used as an icon for
30202                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
30203 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
30204 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
30205 modal             Boolean          False to allow user interaction with the page while the message box is
30206                                    displayed (defaults to true)
30207 msg               String           A string that will replace the existing message box body text (defaults
30208                                    to the XHTML-compliant non-breaking space character '&#160;')
30209 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
30210 progress          Boolean          True to display a progress bar (defaults to false)
30211 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
30212 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
30213 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
30214 title             String           The title text
30215 value             String           The string value to set into the active textbox element if displayed
30216 wait              Boolean          True to display a progress bar (defaults to false)
30217 width             Number           The width of the dialog in pixels
30218 </pre>
30219          *
30220          * Example usage:
30221          * <pre><code>
30222 Roo.Msg.show({
30223    title: 'Address',
30224    msg: 'Please enter your address:',
30225    width: 300,
30226    buttons: Roo.MessageBox.OKCANCEL,
30227    multiline: true,
30228    fn: saveAddress,
30229    animEl: 'addAddressBtn'
30230 });
30231 </code></pre>
30232          * @param {Object} config Configuration options
30233          * @return {Roo.MessageBox} This message box
30234          */
30235         show : function(options)
30236         {
30237             
30238             // this causes nightmares if you show one dialog after another
30239             // especially on callbacks..
30240              
30241             if(this.isVisible()){
30242                 
30243                 this.hide();
30244                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML )
30245                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
30246                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
30247                 
30248             }
30249             var d = this.getDialog();
30250             opt = options;
30251             d.setTitle(opt.title || "&#160;");
30252             d.close.setDisplayed(opt.closable !== false);
30253             activeTextEl = textboxEl;
30254             opt.prompt = opt.prompt || (opt.multiline ? true : false);
30255             if(opt.prompt){
30256                 if(opt.multiline){
30257                     textboxEl.hide();
30258                     textareaEl.show();
30259                     textareaEl.setHeight(typeof opt.multiline == "number" ?
30260                         opt.multiline : this.defaultTextHeight);
30261                     activeTextEl = textareaEl;
30262                 }else{
30263                     textboxEl.show();
30264                     textareaEl.hide();
30265                 }
30266             }else{
30267                 textboxEl.hide();
30268                 textareaEl.hide();
30269             }
30270             progressEl.setDisplayed(opt.progress === true);
30271             this.updateProgress(0);
30272             activeTextEl.dom.value = opt.value || "";
30273             if(opt.prompt){
30274                 dlg.setDefaultButton(activeTextEl);
30275             }else{
30276                 var bs = opt.buttons;
30277                 var db = null;
30278                 if(bs && bs.ok){
30279                     db = buttons["ok"];
30280                 }else if(bs && bs.yes){
30281                     db = buttons["yes"];
30282                 }
30283                 dlg.setDefaultButton(db);
30284             }
30285             bwidth = updateButtons(opt.buttons);
30286             this.updateText(opt.msg);
30287             if(opt.cls){
30288                 d.el.addClass(opt.cls);
30289             }
30290             d.proxyDrag = opt.proxyDrag === true;
30291             d.modal = opt.modal !== false;
30292             d.mask = opt.modal !== false ? mask : false;
30293             if(!d.isVisible()){
30294                 // force it to the end of the z-index stack so it gets a cursor in FF
30295                 document.body.appendChild(dlg.el.dom);
30296                 d.animateTarget = null;
30297                 d.show(options.animEl);
30298             }
30299             return this;
30300         },
30301
30302         /**
30303          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
30304          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
30305          * and closing the message box when the process is complete.
30306          * @param {String} title The title bar text
30307          * @param {String} msg The message box body text
30308          * @return {Roo.MessageBox} This message box
30309          */
30310         progress : function(title, msg){
30311             this.show({
30312                 title : title,
30313                 msg : msg,
30314                 buttons: false,
30315                 progress:true,
30316                 closable:false,
30317                 minWidth: this.minProgressWidth,
30318                 modal : true
30319             });
30320             return this;
30321         },
30322
30323         /**
30324          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
30325          * If a callback function is passed it will be called after the user clicks the button, and the
30326          * id of the button that was clicked will be passed as the only parameter to the callback
30327          * (could also be the top-right close button).
30328          * @param {String} title The title bar text
30329          * @param {String} msg The message box body text
30330          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30331          * @param {Object} scope (optional) The scope of the callback function
30332          * @return {Roo.MessageBox} This message box
30333          */
30334         alert : function(title, msg, fn, scope){
30335             this.show({
30336                 title : title,
30337                 msg : msg,
30338                 buttons: this.OK,
30339                 fn: fn,
30340                 scope : scope,
30341                 modal : true
30342             });
30343             return this;
30344         },
30345
30346         /**
30347          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
30348          * interaction while waiting for a long-running process to complete that does not have defined intervals.
30349          * You are responsible for closing the message box when the process is complete.
30350          * @param {String} msg The message box body text
30351          * @param {String} title (optional) The title bar text
30352          * @return {Roo.MessageBox} This message box
30353          */
30354         wait : function(msg, title){
30355             this.show({
30356                 title : title,
30357                 msg : msg,
30358                 buttons: false,
30359                 closable:false,
30360                 progress:true,
30361                 modal:true,
30362                 width:300,
30363                 wait:true
30364             });
30365             waitTimer = Roo.TaskMgr.start({
30366                 run: function(i){
30367                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
30368                 },
30369                 interval: 1000
30370             });
30371             return this;
30372         },
30373
30374         /**
30375          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
30376          * If a callback function is passed it will be called after the user clicks either button, and the id of the
30377          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
30378          * @param {String} title The title bar text
30379          * @param {String} msg The message box body text
30380          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30381          * @param {Object} scope (optional) The scope of the callback function
30382          * @return {Roo.MessageBox} This message box
30383          */
30384         confirm : function(title, msg, fn, scope){
30385             this.show({
30386                 title : title,
30387                 msg : msg,
30388                 buttons: this.YESNO,
30389                 fn: fn,
30390                 scope : scope,
30391                 modal : true
30392             });
30393             return this;
30394         },
30395
30396         /**
30397          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
30398          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
30399          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
30400          * (could also be the top-right close button) and the text that was entered will be passed as the two
30401          * parameters to the callback.
30402          * @param {String} title The title bar text
30403          * @param {String} msg The message box body text
30404          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30405          * @param {Object} scope (optional) The scope of the callback function
30406          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
30407          * property, or the height in pixels to create the textbox (defaults to false / single-line)
30408          * @return {Roo.MessageBox} This message box
30409          */
30410         prompt : function(title, msg, fn, scope, multiline){
30411             this.show({
30412                 title : title,
30413                 msg : msg,
30414                 buttons: this.OKCANCEL,
30415                 fn: fn,
30416                 minWidth:250,
30417                 scope : scope,
30418                 prompt:true,
30419                 multiline: multiline,
30420                 modal : true
30421             });
30422             return this;
30423         },
30424
30425         /**
30426          * Button config that displays a single OK button
30427          * @type Object
30428          */
30429         OK : {ok:true},
30430         /**
30431          * Button config that displays Yes and No buttons
30432          * @type Object
30433          */
30434         YESNO : {yes:true, no:true},
30435         /**
30436          * Button config that displays OK and Cancel buttons
30437          * @type Object
30438          */
30439         OKCANCEL : {ok:true, cancel:true},
30440         /**
30441          * Button config that displays Yes, No and Cancel buttons
30442          * @type Object
30443          */
30444         YESNOCANCEL : {yes:true, no:true, cancel:true},
30445
30446         /**
30447          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
30448          * @type Number
30449          */
30450         defaultTextHeight : 75,
30451         /**
30452          * The maximum width in pixels of the message box (defaults to 600)
30453          * @type Number
30454          */
30455         maxWidth : 600,
30456         /**
30457          * The minimum width in pixels of the message box (defaults to 100)
30458          * @type Number
30459          */
30460         minWidth : 100,
30461         /**
30462          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
30463          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
30464          * @type Number
30465          */
30466         minProgressWidth : 250,
30467         /**
30468          * An object containing the default button text strings that can be overriden for localized language support.
30469          * Supported properties are: ok, cancel, yes and no.
30470          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
30471          * @type Object
30472          */
30473         buttonText : {
30474             ok : "OK",
30475             cancel : "Cancel",
30476             yes : "Yes",
30477             no : "No"
30478         }
30479     };
30480 }();
30481
30482 /**
30483  * Shorthand for {@link Roo.MessageBox}
30484  */
30485 Roo.Msg = Roo.MessageBox;/*
30486  * Based on:
30487  * Ext JS Library 1.1.1
30488  * Copyright(c) 2006-2007, Ext JS, LLC.
30489  *
30490  * Originally Released Under LGPL - original licence link has changed is not relivant.
30491  *
30492  * Fork - LGPL
30493  * <script type="text/javascript">
30494  */
30495 /**
30496  * @class Roo.QuickTips
30497  * Provides attractive and customizable tooltips for any element.
30498  * @singleton
30499  */
30500 Roo.QuickTips = function(){
30501     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
30502     var ce, bd, xy, dd;
30503     var visible = false, disabled = true, inited = false;
30504     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
30505     
30506     var onOver = function(e){
30507         if(disabled){
30508             return;
30509         }
30510         var t = e.getTarget();
30511         if(!t || t.nodeType !== 1 || t == document || t == document.body){
30512             return;
30513         }
30514         if(ce && t == ce.el){
30515             clearTimeout(hideProc);
30516             return;
30517         }
30518         if(t && tagEls[t.id]){
30519             tagEls[t.id].el = t;
30520             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
30521             return;
30522         }
30523         var ttp, et = Roo.fly(t);
30524         var ns = cfg.namespace;
30525         if(tm.interceptTitles && t.title){
30526             ttp = t.title;
30527             t.qtip = ttp;
30528             t.removeAttribute("title");
30529             e.preventDefault();
30530         }else{
30531             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
30532         }
30533         if(ttp){
30534             showProc = show.defer(tm.showDelay, tm, [{
30535                 el: t, 
30536                 text: ttp, 
30537                 width: et.getAttributeNS(ns, cfg.width),
30538                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
30539                 title: et.getAttributeNS(ns, cfg.title),
30540                     cls: et.getAttributeNS(ns, cfg.cls)
30541             }]);
30542         }
30543     };
30544     
30545     var onOut = function(e){
30546         clearTimeout(showProc);
30547         var t = e.getTarget();
30548         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
30549             hideProc = setTimeout(hide, tm.hideDelay);
30550         }
30551     };
30552     
30553     var onMove = function(e){
30554         if(disabled){
30555             return;
30556         }
30557         xy = e.getXY();
30558         xy[1] += 18;
30559         if(tm.trackMouse && ce){
30560             el.setXY(xy);
30561         }
30562     };
30563     
30564     var onDown = function(e){
30565         clearTimeout(showProc);
30566         clearTimeout(hideProc);
30567         if(!e.within(el)){
30568             if(tm.hideOnClick){
30569                 hide();
30570                 tm.disable();
30571                 tm.enable.defer(100, tm);
30572             }
30573         }
30574     };
30575     
30576     var getPad = function(){
30577         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
30578     };
30579
30580     var show = function(o){
30581         if(disabled){
30582             return;
30583         }
30584         clearTimeout(dismissProc);
30585         ce = o;
30586         if(removeCls){ // in case manually hidden
30587             el.removeClass(removeCls);
30588             removeCls = null;
30589         }
30590         if(ce.cls){
30591             el.addClass(ce.cls);
30592             removeCls = ce.cls;
30593         }
30594         if(ce.title){
30595             tipTitle.update(ce.title);
30596             tipTitle.show();
30597         }else{
30598             tipTitle.update('');
30599             tipTitle.hide();
30600         }
30601         el.dom.style.width  = tm.maxWidth+'px';
30602         //tipBody.dom.style.width = '';
30603         tipBodyText.update(o.text);
30604         var p = getPad(), w = ce.width;
30605         if(!w){
30606             var td = tipBodyText.dom;
30607             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
30608             if(aw > tm.maxWidth){
30609                 w = tm.maxWidth;
30610             }else if(aw < tm.minWidth){
30611                 w = tm.minWidth;
30612             }else{
30613                 w = aw;
30614             }
30615         }
30616         //tipBody.setWidth(w);
30617         el.setWidth(parseInt(w, 10) + p);
30618         if(ce.autoHide === false){
30619             close.setDisplayed(true);
30620             if(dd){
30621                 dd.unlock();
30622             }
30623         }else{
30624             close.setDisplayed(false);
30625             if(dd){
30626                 dd.lock();
30627             }
30628         }
30629         if(xy){
30630             el.avoidY = xy[1]-18;
30631             el.setXY(xy);
30632         }
30633         if(tm.animate){
30634             el.setOpacity(.1);
30635             el.setStyle("visibility", "visible");
30636             el.fadeIn({callback: afterShow});
30637         }else{
30638             afterShow();
30639         }
30640     };
30641     
30642     var afterShow = function(){
30643         if(ce){
30644             el.show();
30645             esc.enable();
30646             if(tm.autoDismiss && ce.autoHide !== false){
30647                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
30648             }
30649         }
30650     };
30651     
30652     var hide = function(noanim){
30653         clearTimeout(dismissProc);
30654         clearTimeout(hideProc);
30655         ce = null;
30656         if(el.isVisible()){
30657             esc.disable();
30658             if(noanim !== true && tm.animate){
30659                 el.fadeOut({callback: afterHide});
30660             }else{
30661                 afterHide();
30662             } 
30663         }
30664     };
30665     
30666     var afterHide = function(){
30667         el.hide();
30668         if(removeCls){
30669             el.removeClass(removeCls);
30670             removeCls = null;
30671         }
30672     };
30673     
30674     return {
30675         /**
30676         * @cfg {Number} minWidth
30677         * The minimum width of the quick tip (defaults to 40)
30678         */
30679        minWidth : 40,
30680         /**
30681         * @cfg {Number} maxWidth
30682         * The maximum width of the quick tip (defaults to 300)
30683         */
30684        maxWidth : 300,
30685         /**
30686         * @cfg {Boolean} interceptTitles
30687         * True to automatically use the element's DOM title value if available (defaults to false)
30688         */
30689        interceptTitles : false,
30690         /**
30691         * @cfg {Boolean} trackMouse
30692         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
30693         */
30694        trackMouse : false,
30695         /**
30696         * @cfg {Boolean} hideOnClick
30697         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
30698         */
30699        hideOnClick : true,
30700         /**
30701         * @cfg {Number} showDelay
30702         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
30703         */
30704        showDelay : 500,
30705         /**
30706         * @cfg {Number} hideDelay
30707         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
30708         */
30709        hideDelay : 200,
30710         /**
30711         * @cfg {Boolean} autoHide
30712         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
30713         * Used in conjunction with hideDelay.
30714         */
30715        autoHide : true,
30716         /**
30717         * @cfg {Boolean}
30718         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
30719         * (defaults to true).  Used in conjunction with autoDismissDelay.
30720         */
30721        autoDismiss : true,
30722         /**
30723         * @cfg {Number}
30724         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
30725         */
30726        autoDismissDelay : 5000,
30727        /**
30728         * @cfg {Boolean} animate
30729         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
30730         */
30731        animate : false,
30732
30733        /**
30734         * @cfg {String} title
30735         * Title text to display (defaults to '').  This can be any valid HTML markup.
30736         */
30737         title: '',
30738        /**
30739         * @cfg {String} text
30740         * Body text to display (defaults to '').  This can be any valid HTML markup.
30741         */
30742         text : '',
30743        /**
30744         * @cfg {String} cls
30745         * A CSS class to apply to the base quick tip element (defaults to '').
30746         */
30747         cls : '',
30748        /**
30749         * @cfg {Number} width
30750         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
30751         * minWidth or maxWidth.
30752         */
30753         width : null,
30754
30755     /**
30756      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
30757      * or display QuickTips in a page.
30758      */
30759        init : function(){
30760           tm = Roo.QuickTips;
30761           cfg = tm.tagConfig;
30762           if(!inited){
30763               if(!Roo.isReady){ // allow calling of init() before onReady
30764                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
30765                   return;
30766               }
30767               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
30768               el.fxDefaults = {stopFx: true};
30769               // maximum custom styling
30770               //el.update('<div class="x-tip-top-left"><div class="x-tip-top-right"><div class="x-tip-top"></div></div></div><div class="x-tip-bd-left"><div class="x-tip-bd-right"><div class="x-tip-bd"><div class="x-tip-close"></div><h3></h3><div class="x-tip-bd-inner"></div><div class="x-clear"></div></div></div></div><div class="x-tip-ft-left"><div class="x-tip-ft-right"><div class="x-tip-ft"></div></div></div>');
30771               el.update('<div class="x-tip-bd"><div class="x-tip-close"></div><h3></h3><div class="x-tip-bd-inner"></div><div class="x-clear"></div></div>');              
30772               tipTitle = el.child('h3');
30773               tipTitle.enableDisplayMode("block");
30774               tipBody = el.child('div.x-tip-bd');
30775               tipBodyText = el.child('div.x-tip-bd-inner');
30776               //bdLeft = el.child('div.x-tip-bd-left');
30777               //bdRight = el.child('div.x-tip-bd-right');
30778               close = el.child('div.x-tip-close');
30779               close.enableDisplayMode("block");
30780               close.on("click", hide);
30781               var d = Roo.get(document);
30782               d.on("mousedown", onDown);
30783               d.on("mouseover", onOver);
30784               d.on("mouseout", onOut);
30785               d.on("mousemove", onMove);
30786               esc = d.addKeyListener(27, hide);
30787               esc.disable();
30788               if(Roo.dd.DD){
30789                   dd = el.initDD("default", null, {
30790                       onDrag : function(){
30791                           el.sync();  
30792                       }
30793                   });
30794                   dd.setHandleElId(tipTitle.id);
30795                   dd.lock();
30796               }
30797               inited = true;
30798           }
30799           this.enable(); 
30800        },
30801
30802     /**
30803      * Configures a new quick tip instance and assigns it to a target element.  The following config options
30804      * are supported:
30805      * <pre>
30806 Property    Type                   Description
30807 ----------  ---------------------  ------------------------------------------------------------------------
30808 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
30809      * </ul>
30810      * @param {Object} config The config object
30811      */
30812        register : function(config){
30813            var cs = config instanceof Array ? config : arguments;
30814            for(var i = 0, len = cs.length; i < len; i++) {
30815                var c = cs[i];
30816                var target = c.target;
30817                if(target){
30818                    if(target instanceof Array){
30819                        for(var j = 0, jlen = target.length; j < jlen; j++){
30820                            tagEls[target[j]] = c;
30821                        }
30822                    }else{
30823                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
30824                    }
30825                }
30826            }
30827        },
30828
30829     /**
30830      * Removes this quick tip from its element and destroys it.
30831      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
30832      */
30833        unregister : function(el){
30834            delete tagEls[Roo.id(el)];
30835        },
30836
30837     /**
30838      * Enable this quick tip.
30839      */
30840        enable : function(){
30841            if(inited && disabled){
30842                locks.pop();
30843                if(locks.length < 1){
30844                    disabled = false;
30845                }
30846            }
30847        },
30848
30849     /**
30850      * Disable this quick tip.
30851      */
30852        disable : function(){
30853           disabled = true;
30854           clearTimeout(showProc);
30855           clearTimeout(hideProc);
30856           clearTimeout(dismissProc);
30857           if(ce){
30858               hide(true);
30859           }
30860           locks.push(1);
30861        },
30862
30863     /**
30864      * Returns true if the quick tip is enabled, else false.
30865      */
30866        isEnabled : function(){
30867             return !disabled;
30868        },
30869
30870         // private
30871        tagConfig : {
30872            namespace : "ext",
30873            attribute : "qtip",
30874            width : "width",
30875            target : "target",
30876            title : "qtitle",
30877            hide : "hide",
30878            cls : "qclass"
30879        }
30880    };
30881 }();
30882
30883 // backwards compat
30884 Roo.QuickTips.tips = Roo.QuickTips.register;/*
30885  * Based on:
30886  * Ext JS Library 1.1.1
30887  * Copyright(c) 2006-2007, Ext JS, LLC.
30888  *
30889  * Originally Released Under LGPL - original licence link has changed is not relivant.
30890  *
30891  * Fork - LGPL
30892  * <script type="text/javascript">
30893  */
30894  
30895
30896 /**
30897  * @class Roo.tree.TreePanel
30898  * @extends Roo.data.Tree
30899
30900  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
30901  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
30902  * @cfg {Boolean} enableDD true to enable drag and drop
30903  * @cfg {Boolean} enableDrag true to enable just drag
30904  * @cfg {Boolean} enableDrop true to enable just drop
30905  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
30906  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
30907  * @cfg {String} ddGroup The DD group this TreePanel belongs to
30908  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
30909  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
30910  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
30911  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
30912  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
30913  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
30914  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
30915  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
30916  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
30917  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
30918  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
30919  * @cfg {Function} renderer DEPRECATED - use TreeLoader:create event / Sets the rendering (formatting) function for the nodes. to return HTML markup for the tree view. The render function is called with  the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
30920  * @cfg {Function} rendererTip DEPRECATED - use TreeLoader:create event / Sets the rendering (formatting) function for the nodes hovertip to return HTML markup for the tree view. The render function is called with  the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
30921  * 
30922  * @constructor
30923  * @param {String/HTMLElement/Element} el The container element
30924  * @param {Object} config
30925  */
30926 Roo.tree.TreePanel = function(el, config){
30927     var root = false;
30928     var loader = false;
30929     if (config.root) {
30930         root = config.root;
30931         delete config.root;
30932     }
30933     if (config.loader) {
30934         loader = config.loader;
30935         delete config.loader;
30936     }
30937     
30938     Roo.apply(this, config);
30939     Roo.tree.TreePanel.superclass.constructor.call(this);
30940     this.el = Roo.get(el);
30941     this.el.addClass('x-tree');
30942     //console.log(root);
30943     if (root) {
30944         this.setRootNode( Roo.factory(root, Roo.tree));
30945     }
30946     if (loader) {
30947         this.loader = Roo.factory(loader, Roo.tree);
30948     }
30949    /**
30950     * Read-only. The id of the container element becomes this TreePanel's id.
30951     */
30952     this.id = this.el.id;
30953     this.addEvents({
30954         /**
30955         * @event beforeload
30956         * Fires before a node is loaded, return false to cancel
30957         * @param {Node} node The node being loaded
30958         */
30959         "beforeload" : true,
30960         /**
30961         * @event load
30962         * Fires when a node is loaded
30963         * @param {Node} node The node that was loaded
30964         */
30965         "load" : true,
30966         /**
30967         * @event textchange
30968         * Fires when the text for a node is changed
30969         * @param {Node} node The node
30970         * @param {String} text The new text
30971         * @param {String} oldText The old text
30972         */
30973         "textchange" : true,
30974         /**
30975         * @event beforeexpand
30976         * Fires before a node is expanded, return false to cancel.
30977         * @param {Node} node The node
30978         * @param {Boolean} deep
30979         * @param {Boolean} anim
30980         */
30981         "beforeexpand" : true,
30982         /**
30983         * @event beforecollapse
30984         * Fires before a node is collapsed, return false to cancel.
30985         * @param {Node} node The node
30986         * @param {Boolean} deep
30987         * @param {Boolean} anim
30988         */
30989         "beforecollapse" : true,
30990         /**
30991         * @event expand
30992         * Fires when a node is expanded
30993         * @param {Node} node The node
30994         */
30995         "expand" : true,
30996         /**
30997         * @event disabledchange
30998         * Fires when the disabled status of a node changes
30999         * @param {Node} node The node
31000         * @param {Boolean} disabled
31001         */
31002         "disabledchange" : true,
31003         /**
31004         * @event collapse
31005         * Fires when a node is collapsed
31006         * @param {Node} node The node
31007         */
31008         "collapse" : true,
31009         /**
31010         * @event beforeclick
31011         * Fires before click processing on a node. Return false to cancel the default action.
31012         * @param {Node} node The node
31013         * @param {Roo.EventObject} e The event object
31014         */
31015         "beforeclick":true,
31016         /**
31017         * @event checkchange
31018         * Fires when a node with a checkbox's checked property changes
31019         * @param {Node} this This node
31020         * @param {Boolean} checked
31021         */
31022         "checkchange":true,
31023         /**
31024         * @event click
31025         * Fires when a node is clicked
31026         * @param {Node} node The node
31027         * @param {Roo.EventObject} e The event object
31028         */
31029         "click":true,
31030         /**
31031         * @event dblclick
31032         * Fires when a node is double clicked
31033         * @param {Node} node The node
31034         * @param {Roo.EventObject} e The event object
31035         */
31036         "dblclick":true,
31037         /**
31038         * @event contextmenu
31039         * Fires when a node is right clicked
31040         * @param {Node} node The node
31041         * @param {Roo.EventObject} e The event object
31042         */
31043         "contextmenu":true,
31044         /**
31045         * @event beforechildrenrendered
31046         * Fires right before the child nodes for a node are rendered
31047         * @param {Node} node The node
31048         */
31049         "beforechildrenrendered":true,
31050         /**
31051         * @event startdrag
31052         * Fires when a node starts being dragged
31053         * @param {Roo.tree.TreePanel} this
31054         * @param {Roo.tree.TreeNode} node
31055         * @param {event} e The raw browser event
31056         */ 
31057        "startdrag" : true,
31058        /**
31059         * @event enddrag
31060         * Fires when a drag operation is complete
31061         * @param {Roo.tree.TreePanel} this
31062         * @param {Roo.tree.TreeNode} node
31063         * @param {event} e The raw browser event
31064         */
31065        "enddrag" : true,
31066        /**
31067         * @event dragdrop
31068         * Fires when a dragged node is dropped on a valid DD target
31069         * @param {Roo.tree.TreePanel} this
31070         * @param {Roo.tree.TreeNode} node
31071         * @param {DD} dd The dd it was dropped on
31072         * @param {event} e The raw browser event
31073         */
31074        "dragdrop" : true,
31075        /**
31076         * @event beforenodedrop
31077         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
31078         * passed to handlers has the following properties:<br />
31079         * <ul style="padding:5px;padding-left:16px;">
31080         * <li>tree - The TreePanel</li>
31081         * <li>target - The node being targeted for the drop</li>
31082         * <li>data - The drag data from the drag source</li>
31083         * <li>point - The point of the drop - append, above or below</li>
31084         * <li>source - The drag source</li>
31085         * <li>rawEvent - Raw mouse event</li>
31086         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
31087         * to be inserted by setting them on this object.</li>
31088         * <li>cancel - Set this to true to cancel the drop.</li>
31089         * </ul>
31090         * @param {Object} dropEvent
31091         */
31092        "beforenodedrop" : true,
31093        /**
31094         * @event nodedrop
31095         * Fires after a DD object is dropped on a node in this tree. The dropEvent
31096         * passed to handlers has the following properties:<br />
31097         * <ul style="padding:5px;padding-left:16px;">
31098         * <li>tree - The TreePanel</li>
31099         * <li>target - The node being targeted for the drop</li>
31100         * <li>data - The drag data from the drag source</li>
31101         * <li>point - The point of the drop - append, above or below</li>
31102         * <li>source - The drag source</li>
31103         * <li>rawEvent - Raw mouse event</li>
31104         * <li>dropNode - Dropped node(s).</li>
31105         * </ul>
31106         * @param {Object} dropEvent
31107         */
31108        "nodedrop" : true,
31109         /**
31110         * @event nodedragover
31111         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
31112         * passed to handlers has the following properties:<br />
31113         * <ul style="padding:5px;padding-left:16px;">
31114         * <li>tree - The TreePanel</li>
31115         * <li>target - The node being targeted for the drop</li>
31116         * <li>data - The drag data from the drag source</li>
31117         * <li>point - The point of the drop - append, above or below</li>
31118         * <li>source - The drag source</li>
31119         * <li>rawEvent - Raw mouse event</li>
31120         * <li>dropNode - Drop node(s) provided by the source.</li>
31121         * <li>cancel - Set this to true to signal drop not allowed.</li>
31122         * </ul>
31123         * @param {Object} dragOverEvent
31124         */
31125        "nodedragover" : true
31126         
31127     });
31128     if(this.singleExpand){
31129        this.on("beforeexpand", this.restrictExpand, this);
31130     }
31131     if (this.editor) {
31132         this.editor.tree = this;
31133         this.editor = Roo.factory(this.editor, Roo.tree);
31134     }
31135    
31136 };
31137 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
31138     rootVisible : true,
31139     animate: Roo.enableFx,
31140     lines : true,
31141     enableDD : false,
31142     hlDrop : Roo.enableFx,
31143   
31144     renderer: false,
31145     
31146     rendererTip: false,
31147     // private
31148     restrictExpand : function(node){
31149         var p = node.parentNode;
31150         if(p){
31151             if(p.expandedChild && p.expandedChild.parentNode == p){
31152                 p.expandedChild.collapse();
31153             }
31154             p.expandedChild = node;
31155         }
31156     },
31157
31158     // private override
31159     setRootNode : function(node){
31160         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
31161         if(!this.rootVisible){
31162             node.ui = new Roo.tree.RootTreeNodeUI(node);
31163         }
31164         return node;
31165     },
31166
31167     /**
31168      * Returns the container element for this TreePanel
31169      */
31170     getEl : function(){
31171         return this.el;
31172     },
31173
31174     /**
31175      * Returns the default TreeLoader for this TreePanel
31176      */
31177     getLoader : function(){
31178         return this.loader;
31179     },
31180
31181     /**
31182      * Expand all nodes
31183      */
31184     expandAll : function(){
31185         this.root.expand(true);
31186     },
31187
31188     /**
31189      * Collapse all nodes
31190      */
31191     collapseAll : function(){
31192         this.root.collapse(true);
31193     },
31194
31195     /**
31196      * Returns the selection model used by this TreePanel
31197      */
31198     getSelectionModel : function(){
31199         if(!this.selModel){
31200             this.selModel = new Roo.tree.DefaultSelectionModel();
31201         }
31202         return this.selModel;
31203     },
31204
31205     /**
31206      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
31207      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
31208      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
31209      * @return {Array}
31210      */
31211     getChecked : function(a, startNode){
31212         startNode = startNode || this.root;
31213         var r = [];
31214         var f = function(){
31215             if(this.attributes.checked){
31216                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
31217             }
31218         }
31219         startNode.cascade(f);
31220         return r;
31221     },
31222
31223     /**
31224      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
31225      * @param {String} path
31226      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
31227      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
31228      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
31229      */
31230     expandPath : function(path, attr, callback){
31231         attr = attr || "id";
31232         var keys = path.split(this.pathSeparator);
31233         var curNode = this.root;
31234         if(curNode.attributes[attr] != keys[1]){ // invalid root
31235             if(callback){
31236                 callback(false, null);
31237             }
31238             return;
31239         }
31240         var index = 1;
31241         var f = function(){
31242             if(++index == keys.length){
31243                 if(callback){
31244                     callback(true, curNode);
31245                 }
31246                 return;
31247             }
31248             var c = curNode.findChild(attr, keys[index]);
31249             if(!c){
31250                 if(callback){
31251                     callback(false, curNode);
31252                 }
31253                 return;
31254             }
31255             curNode = c;
31256             c.expand(false, false, f);
31257         };
31258         curNode.expand(false, false, f);
31259     },
31260
31261     /**
31262      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
31263      * @param {String} path
31264      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
31265      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
31266      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
31267      */
31268     selectPath : function(path, attr, callback){
31269         attr = attr || "id";
31270         var keys = path.split(this.pathSeparator);
31271         var v = keys.pop();
31272         if(keys.length > 0){
31273             var f = function(success, node){
31274                 if(success && node){
31275                     var n = node.findChild(attr, v);
31276                     if(n){
31277                         n.select();
31278                         if(callback){
31279                             callback(true, n);
31280                         }
31281                     }else if(callback){
31282                         callback(false, n);
31283                     }
31284                 }else{
31285                     if(callback){
31286                         callback(false, n);
31287                     }
31288                 }
31289             };
31290             this.expandPath(keys.join(this.pathSeparator), attr, f);
31291         }else{
31292             this.root.select();
31293             if(callback){
31294                 callback(true, this.root);
31295             }
31296         }
31297     },
31298
31299     getTreeEl : function(){
31300         return this.el;
31301     },
31302
31303     /**
31304      * Trigger rendering of this TreePanel
31305      */
31306     render : function(){
31307         if (this.innerCt) {
31308             return this; // stop it rendering more than once!!
31309         }
31310         
31311         this.innerCt = this.el.createChild({tag:"ul",
31312                cls:"x-tree-root-ct " +
31313                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
31314
31315         if(this.containerScroll){
31316             Roo.dd.ScrollManager.register(this.el);
31317         }
31318         if((this.enableDD || this.enableDrop) && !this.dropZone){
31319            /**
31320             * The dropZone used by this tree if drop is enabled
31321             * @type Roo.tree.TreeDropZone
31322             */
31323              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
31324                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
31325            });
31326         }
31327         if((this.enableDD || this.enableDrag) && !this.dragZone){
31328            /**
31329             * The dragZone used by this tree if drag is enabled
31330             * @type Roo.tree.TreeDragZone
31331             */
31332             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
31333                ddGroup: this.ddGroup || "TreeDD",
31334                scroll: this.ddScroll
31335            });
31336         }
31337         this.getSelectionModel().init(this);
31338         if (!this.root) {
31339             console.log("ROOT not set in tree");
31340             return;
31341         }
31342         this.root.render();
31343         if(!this.rootVisible){
31344             this.root.renderChildren();
31345         }
31346         return this;
31347     }
31348 });/*
31349  * Based on:
31350  * Ext JS Library 1.1.1
31351  * Copyright(c) 2006-2007, Ext JS, LLC.
31352  *
31353  * Originally Released Under LGPL - original licence link has changed is not relivant.
31354  *
31355  * Fork - LGPL
31356  * <script type="text/javascript">
31357  */
31358  
31359
31360 /**
31361  * @class Roo.tree.DefaultSelectionModel
31362  * @extends Roo.util.Observable
31363  * The default single selection for a TreePanel.
31364  */
31365 Roo.tree.DefaultSelectionModel = function(){
31366    this.selNode = null;
31367    
31368    this.addEvents({
31369        /**
31370         * @event selectionchange
31371         * Fires when the selected node changes
31372         * @param {DefaultSelectionModel} this
31373         * @param {TreeNode} node the new selection
31374         */
31375        "selectionchange" : true,
31376
31377        /**
31378         * @event beforeselect
31379         * Fires before the selected node changes, return false to cancel the change
31380         * @param {DefaultSelectionModel} this
31381         * @param {TreeNode} node the new selection
31382         * @param {TreeNode} node the old selection
31383         */
31384        "beforeselect" : true
31385    });
31386 };
31387
31388 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
31389     init : function(tree){
31390         this.tree = tree;
31391         tree.getTreeEl().on("keydown", this.onKeyDown, this);
31392         tree.on("click", this.onNodeClick, this);
31393     },
31394     
31395     onNodeClick : function(node, e){
31396         if (e.ctrlKey && this.selNode == node)  {
31397             this.unselect(node);
31398             return;
31399         }
31400         this.select(node);
31401     },
31402     
31403     /**
31404      * Select a node.
31405      * @param {TreeNode} node The node to select
31406      * @return {TreeNode} The selected node
31407      */
31408     select : function(node){
31409         var last = this.selNode;
31410         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
31411             if(last){
31412                 last.ui.onSelectedChange(false);
31413             }
31414             this.selNode = node;
31415             node.ui.onSelectedChange(true);
31416             this.fireEvent("selectionchange", this, node, last);
31417         }
31418         return node;
31419     },
31420     
31421     /**
31422      * Deselect a node.
31423      * @param {TreeNode} node The node to unselect
31424      */
31425     unselect : function(node){
31426         if(this.selNode == node){
31427             this.clearSelections();
31428         }    
31429     },
31430     
31431     /**
31432      * Clear all selections
31433      */
31434     clearSelections : function(){
31435         var n = this.selNode;
31436         if(n){
31437             n.ui.onSelectedChange(false);
31438             this.selNode = null;
31439             this.fireEvent("selectionchange", this, null);
31440         }
31441         return n;
31442     },
31443     
31444     /**
31445      * Get the selected node
31446      * @return {TreeNode} The selected node
31447      */
31448     getSelectedNode : function(){
31449         return this.selNode;    
31450     },
31451     
31452     /**
31453      * Returns true if the node is selected
31454      * @param {TreeNode} node The node to check
31455      * @return {Boolean}
31456      */
31457     isSelected : function(node){
31458         return this.selNode == node;  
31459     },
31460
31461     /**
31462      * Selects the node above the selected node in the tree, intelligently walking the nodes
31463      * @return TreeNode The new selection
31464      */
31465     selectPrevious : function(){
31466         var s = this.selNode || this.lastSelNode;
31467         if(!s){
31468             return null;
31469         }
31470         var ps = s.previousSibling;
31471         if(ps){
31472             if(!ps.isExpanded() || ps.childNodes.length < 1){
31473                 return this.select(ps);
31474             } else{
31475                 var lc = ps.lastChild;
31476                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
31477                     lc = lc.lastChild;
31478                 }
31479                 return this.select(lc);
31480             }
31481         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
31482             return this.select(s.parentNode);
31483         }
31484         return null;
31485     },
31486
31487     /**
31488      * Selects the node above the selected node in the tree, intelligently walking the nodes
31489      * @return TreeNode The new selection
31490      */
31491     selectNext : function(){
31492         var s = this.selNode || this.lastSelNode;
31493         if(!s){
31494             return null;
31495         }
31496         if(s.firstChild && s.isExpanded()){
31497              return this.select(s.firstChild);
31498          }else if(s.nextSibling){
31499              return this.select(s.nextSibling);
31500          }else if(s.parentNode){
31501             var newS = null;
31502             s.parentNode.bubble(function(){
31503                 if(this.nextSibling){
31504                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
31505                     return false;
31506                 }
31507             });
31508             return newS;
31509          }
31510         return null;
31511     },
31512
31513     onKeyDown : function(e){
31514         var s = this.selNode || this.lastSelNode;
31515         // undesirable, but required
31516         var sm = this;
31517         if(!s){
31518             return;
31519         }
31520         var k = e.getKey();
31521         switch(k){
31522              case e.DOWN:
31523                  e.stopEvent();
31524                  this.selectNext();
31525              break;
31526              case e.UP:
31527                  e.stopEvent();
31528                  this.selectPrevious();
31529              break;
31530              case e.RIGHT:
31531                  e.preventDefault();
31532                  if(s.hasChildNodes()){
31533                      if(!s.isExpanded()){
31534                          s.expand();
31535                      }else if(s.firstChild){
31536                          this.select(s.firstChild, e);
31537                      }
31538                  }
31539              break;
31540              case e.LEFT:
31541                  e.preventDefault();
31542                  if(s.hasChildNodes() && s.isExpanded()){
31543                      s.collapse();
31544                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
31545                      this.select(s.parentNode, e);
31546                  }
31547              break;
31548         };
31549     }
31550 });
31551
31552 /**
31553  * @class Roo.tree.MultiSelectionModel
31554  * @extends Roo.util.Observable
31555  * Multi selection for a TreePanel.
31556  */
31557 Roo.tree.MultiSelectionModel = function(){
31558    this.selNodes = [];
31559    this.selMap = {};
31560    this.addEvents({
31561        /**
31562         * @event selectionchange
31563         * Fires when the selected nodes change
31564         * @param {MultiSelectionModel} this
31565         * @param {Array} nodes Array of the selected nodes
31566         */
31567        "selectionchange" : true
31568    });
31569 };
31570
31571 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
31572     init : function(tree){
31573         this.tree = tree;
31574         tree.getTreeEl().on("keydown", this.onKeyDown, this);
31575         tree.on("click", this.onNodeClick, this);
31576     },
31577     
31578     onNodeClick : function(node, e){
31579         this.select(node, e, e.ctrlKey);
31580     },
31581     
31582     /**
31583      * Select a node.
31584      * @param {TreeNode} node The node to select
31585      * @param {EventObject} e (optional) An event associated with the selection
31586      * @param {Boolean} keepExisting True to retain existing selections
31587      * @return {TreeNode} The selected node
31588      */
31589     select : function(node, e, keepExisting){
31590         if(keepExisting !== true){
31591             this.clearSelections(true);
31592         }
31593         if(this.isSelected(node)){
31594             this.lastSelNode = node;
31595             return node;
31596         }
31597         this.selNodes.push(node);
31598         this.selMap[node.id] = node;
31599         this.lastSelNode = node;
31600         node.ui.onSelectedChange(true);
31601         this.fireEvent("selectionchange", this, this.selNodes);
31602         return node;
31603     },
31604     
31605     /**
31606      * Deselect a node.
31607      * @param {TreeNode} node The node to unselect
31608      */
31609     unselect : function(node){
31610         if(this.selMap[node.id]){
31611             node.ui.onSelectedChange(false);
31612             var sn = this.selNodes;
31613             var index = -1;
31614             if(sn.indexOf){
31615                 index = sn.indexOf(node);
31616             }else{
31617                 for(var i = 0, len = sn.length; i < len; i++){
31618                     if(sn[i] == node){
31619                         index = i;
31620                         break;
31621                     }
31622                 }
31623             }
31624             if(index != -1){
31625                 this.selNodes.splice(index, 1);
31626             }
31627             delete this.selMap[node.id];
31628             this.fireEvent("selectionchange", this, this.selNodes);
31629         }
31630     },
31631     
31632     /**
31633      * Clear all selections
31634      */
31635     clearSelections : function(suppressEvent){
31636         var sn = this.selNodes;
31637         if(sn.length > 0){
31638             for(var i = 0, len = sn.length; i < len; i++){
31639                 sn[i].ui.onSelectedChange(false);
31640             }
31641             this.selNodes = [];
31642             this.selMap = {};
31643             if(suppressEvent !== true){
31644                 this.fireEvent("selectionchange", this, this.selNodes);
31645             }
31646         }
31647     },
31648     
31649     /**
31650      * Returns true if the node is selected
31651      * @param {TreeNode} node The node to check
31652      * @return {Boolean}
31653      */
31654     isSelected : function(node){
31655         return this.selMap[node.id] ? true : false;  
31656     },
31657     
31658     /**
31659      * Returns an array of the selected nodes
31660      * @return {Array}
31661      */
31662     getSelectedNodes : function(){
31663         return this.selNodes;    
31664     },
31665
31666     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
31667
31668     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
31669
31670     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
31671 });/*
31672  * Based on:
31673  * Ext JS Library 1.1.1
31674  * Copyright(c) 2006-2007, Ext JS, LLC.
31675  *
31676  * Originally Released Under LGPL - original licence link has changed is not relivant.
31677  *
31678  * Fork - LGPL
31679  * <script type="text/javascript">
31680  */
31681  
31682 /**
31683  * @class Roo.tree.TreeNode
31684  * @extends Roo.data.Node
31685  * @cfg {String} text The text for this node
31686  * @cfg {Boolean} expanded true to start the node expanded
31687  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
31688  * @cfg {Boolean} allowDrop false if this node cannot be drop on
31689  * @cfg {Boolean} disabled true to start the node disabled
31690  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
31691  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
31692  * @cfg {String} cls A css class to be added to the node
31693  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
31694  * @cfg {String} href URL of the link used for the node (defaults to #)
31695  * @cfg {String} hrefTarget target frame for the link
31696  * @cfg {String} qtip An Ext QuickTip for the node
31697  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
31698  * @cfg {Boolean} singleClickExpand True for single click expand on this node
31699  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
31700  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
31701  * (defaults to undefined with no checkbox rendered)
31702  * @constructor
31703  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
31704  */
31705 Roo.tree.TreeNode = function(attributes){
31706     attributes = attributes || {};
31707     if(typeof attributes == "string"){
31708         attributes = {text: attributes};
31709     }
31710     this.childrenRendered = false;
31711     this.rendered = false;
31712     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
31713     this.expanded = attributes.expanded === true;
31714     this.isTarget = attributes.isTarget !== false;
31715     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
31716     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
31717
31718     /**
31719      * Read-only. The text for this node. To change it use setText().
31720      * @type String
31721      */
31722     this.text = attributes.text;
31723     /**
31724      * True if this node is disabled.
31725      * @type Boolean
31726      */
31727     this.disabled = attributes.disabled === true;
31728
31729     this.addEvents({
31730         /**
31731         * @event textchange
31732         * Fires when the text for this node is changed
31733         * @param {Node} this This node
31734         * @param {String} text The new text
31735         * @param {String} oldText The old text
31736         */
31737         "textchange" : true,
31738         /**
31739         * @event beforeexpand
31740         * Fires before this node is expanded, return false to cancel.
31741         * @param {Node} this This node
31742         * @param {Boolean} deep
31743         * @param {Boolean} anim
31744         */
31745         "beforeexpand" : true,
31746         /**
31747         * @event beforecollapse
31748         * Fires before this node is collapsed, return false to cancel.
31749         * @param {Node} this This node
31750         * @param {Boolean} deep
31751         * @param {Boolean} anim
31752         */
31753         "beforecollapse" : true,
31754         /**
31755         * @event expand
31756         * Fires when this node is expanded
31757         * @param {Node} this This node
31758         */
31759         "expand" : true,
31760         /**
31761         * @event disabledchange
31762         * Fires when the disabled status of this node changes
31763         * @param {Node} this This node
31764         * @param {Boolean} disabled
31765         */
31766         "disabledchange" : true,
31767         /**
31768         * @event collapse
31769         * Fires when this node is collapsed
31770         * @param {Node} this This node
31771         */
31772         "collapse" : true,
31773         /**
31774         * @event beforeclick
31775         * Fires before click processing. Return false to cancel the default action.
31776         * @param {Node} this This node
31777         * @param {Roo.EventObject} e The event object
31778         */
31779         "beforeclick":true,
31780         /**
31781         * @event checkchange
31782         * Fires when a node with a checkbox's checked property changes
31783         * @param {Node} this This node
31784         * @param {Boolean} checked
31785         */
31786         "checkchange":true,
31787         /**
31788         * @event click
31789         * Fires when this node is clicked
31790         * @param {Node} this This node
31791         * @param {Roo.EventObject} e The event object
31792         */
31793         "click":true,
31794         /**
31795         * @event dblclick
31796         * Fires when this node is double clicked
31797         * @param {Node} this This node
31798         * @param {Roo.EventObject} e The event object
31799         */
31800         "dblclick":true,
31801         /**
31802         * @event contextmenu
31803         * Fires when this node is right clicked
31804         * @param {Node} this This node
31805         * @param {Roo.EventObject} e The event object
31806         */
31807         "contextmenu":true,
31808         /**
31809         * @event beforechildrenrendered
31810         * Fires right before the child nodes for this node are rendered
31811         * @param {Node} this This node
31812         */
31813         "beforechildrenrendered":true
31814     });
31815
31816     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
31817
31818     /**
31819      * Read-only. The UI for this node
31820      * @type TreeNodeUI
31821      */
31822     this.ui = new uiClass(this);
31823 };
31824 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
31825     preventHScroll: true,
31826     /**
31827      * Returns true if this node is expanded
31828      * @return {Boolean}
31829      */
31830     isExpanded : function(){
31831         return this.expanded;
31832     },
31833
31834     /**
31835      * Returns the UI object for this node
31836      * @return {TreeNodeUI}
31837      */
31838     getUI : function(){
31839         return this.ui;
31840     },
31841
31842     // private override
31843     setFirstChild : function(node){
31844         var of = this.firstChild;
31845         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
31846         if(this.childrenRendered && of && node != of){
31847             of.renderIndent(true, true);
31848         }
31849         if(this.rendered){
31850             this.renderIndent(true, true);
31851         }
31852     },
31853
31854     // private override
31855     setLastChild : function(node){
31856         var ol = this.lastChild;
31857         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
31858         if(this.childrenRendered && ol && node != ol){
31859             ol.renderIndent(true, true);
31860         }
31861         if(this.rendered){
31862             this.renderIndent(true, true);
31863         }
31864     },
31865
31866     // these methods are overridden to provide lazy rendering support
31867     // private override
31868     appendChild : function(){
31869         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
31870         if(node && this.childrenRendered){
31871             node.render();
31872         }
31873         this.ui.updateExpandIcon();
31874         return node;
31875     },
31876
31877     // private override
31878     removeChild : function(node){
31879         this.ownerTree.getSelectionModel().unselect(node);
31880         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
31881         // if it's been rendered remove dom node
31882         if(this.childrenRendered){
31883             node.ui.remove();
31884         }
31885         if(this.childNodes.length < 1){
31886             this.collapse(false, false);
31887         }else{
31888             this.ui.updateExpandIcon();
31889         }
31890         if(!this.firstChild) {
31891             this.childrenRendered = false;
31892         }
31893         return node;
31894     },
31895
31896     // private override
31897     insertBefore : function(node, refNode){
31898         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
31899         if(newNode && refNode && this.childrenRendered){
31900             node.render();
31901         }
31902         this.ui.updateExpandIcon();
31903         return newNode;
31904     },
31905
31906     /**
31907      * Sets the text for this node
31908      * @param {String} text
31909      */
31910     setText : function(text){
31911         var oldText = this.text;
31912         this.text = text;
31913         this.attributes.text = text;
31914         if(this.rendered){ // event without subscribing
31915             this.ui.onTextChange(this, text, oldText);
31916         }
31917         this.fireEvent("textchange", this, text, oldText);
31918     },
31919
31920     /**
31921      * Triggers selection of this node
31922      */
31923     select : function(){
31924         this.getOwnerTree().getSelectionModel().select(this);
31925     },
31926
31927     /**
31928      * Triggers deselection of this node
31929      */
31930     unselect : function(){
31931         this.getOwnerTree().getSelectionModel().unselect(this);
31932     },
31933
31934     /**
31935      * Returns true if this node is selected
31936      * @return {Boolean}
31937      */
31938     isSelected : function(){
31939         return this.getOwnerTree().getSelectionModel().isSelected(this);
31940     },
31941
31942     /**
31943      * Expand this node.
31944      * @param {Boolean} deep (optional) True to expand all children as well
31945      * @param {Boolean} anim (optional) false to cancel the default animation
31946      * @param {Function} callback (optional) A callback to be called when
31947      * expanding this node completes (does not wait for deep expand to complete).
31948      * Called with 1 parameter, this node.
31949      */
31950     expand : function(deep, anim, callback){
31951         if(!this.expanded){
31952             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
31953                 return;
31954             }
31955             if(!this.childrenRendered){
31956                 this.renderChildren();
31957             }
31958             this.expanded = true;
31959             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
31960                 this.ui.animExpand(function(){
31961                     this.fireEvent("expand", this);
31962                     if(typeof callback == "function"){
31963                         callback(this);
31964                     }
31965                     if(deep === true){
31966                         this.expandChildNodes(true);
31967                     }
31968                 }.createDelegate(this));
31969                 return;
31970             }else{
31971                 this.ui.expand();
31972                 this.fireEvent("expand", this);
31973                 if(typeof callback == "function"){
31974                     callback(this);
31975                 }
31976             }
31977         }else{
31978            if(typeof callback == "function"){
31979                callback(this);
31980            }
31981         }
31982         if(deep === true){
31983             this.expandChildNodes(true);
31984         }
31985     },
31986
31987     isHiddenRoot : function(){
31988         return this.isRoot && !this.getOwnerTree().rootVisible;
31989     },
31990
31991     /**
31992      * Collapse this node.
31993      * @param {Boolean} deep (optional) True to collapse all children as well
31994      * @param {Boolean} anim (optional) false to cancel the default animation
31995      */
31996     collapse : function(deep, anim){
31997         if(this.expanded && !this.isHiddenRoot()){
31998             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
31999                 return;
32000             }
32001             this.expanded = false;
32002             if((this.getOwnerTree().animate && anim !== false) || anim){
32003                 this.ui.animCollapse(function(){
32004                     this.fireEvent("collapse", this);
32005                     if(deep === true){
32006                         this.collapseChildNodes(true);
32007                     }
32008                 }.createDelegate(this));
32009                 return;
32010             }else{
32011                 this.ui.collapse();
32012                 this.fireEvent("collapse", this);
32013             }
32014         }
32015         if(deep === true){
32016             var cs = this.childNodes;
32017             for(var i = 0, len = cs.length; i < len; i++) {
32018                 cs[i].collapse(true, false);
32019             }
32020         }
32021     },
32022
32023     // private
32024     delayedExpand : function(delay){
32025         if(!this.expandProcId){
32026             this.expandProcId = this.expand.defer(delay, this);
32027         }
32028     },
32029
32030     // private
32031     cancelExpand : function(){
32032         if(this.expandProcId){
32033             clearTimeout(this.expandProcId);
32034         }
32035         this.expandProcId = false;
32036     },
32037
32038     /**
32039      * Toggles expanded/collapsed state of the node
32040      */
32041     toggle : function(){
32042         if(this.expanded){
32043             this.collapse();
32044         }else{
32045             this.expand();
32046         }
32047     },
32048
32049     /**
32050      * Ensures all parent nodes are expanded
32051      */
32052     ensureVisible : function(callback){
32053         var tree = this.getOwnerTree();
32054         tree.expandPath(this.parentNode.getPath(), false, function(){
32055             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
32056             Roo.callback(callback);
32057         }.createDelegate(this));
32058     },
32059
32060     /**
32061      * Expand all child nodes
32062      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
32063      */
32064     expandChildNodes : function(deep){
32065         var cs = this.childNodes;
32066         for(var i = 0, len = cs.length; i < len; i++) {
32067                 cs[i].expand(deep);
32068         }
32069     },
32070
32071     /**
32072      * Collapse all child nodes
32073      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
32074      */
32075     collapseChildNodes : function(deep){
32076         var cs = this.childNodes;
32077         for(var i = 0, len = cs.length; i < len; i++) {
32078                 cs[i].collapse(deep);
32079         }
32080     },
32081
32082     /**
32083      * Disables this node
32084      */
32085     disable : function(){
32086         this.disabled = true;
32087         this.unselect();
32088         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
32089             this.ui.onDisableChange(this, true);
32090         }
32091         this.fireEvent("disabledchange", this, true);
32092     },
32093
32094     /**
32095      * Enables this node
32096      */
32097     enable : function(){
32098         this.disabled = false;
32099         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
32100             this.ui.onDisableChange(this, false);
32101         }
32102         this.fireEvent("disabledchange", this, false);
32103     },
32104
32105     // private
32106     renderChildren : function(suppressEvent){
32107         if(suppressEvent !== false){
32108             this.fireEvent("beforechildrenrendered", this);
32109         }
32110         var cs = this.childNodes;
32111         for(var i = 0, len = cs.length; i < len; i++){
32112             cs[i].render(true);
32113         }
32114         this.childrenRendered = true;
32115     },
32116
32117     // private
32118     sort : function(fn, scope){
32119         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
32120         if(this.childrenRendered){
32121             var cs = this.childNodes;
32122             for(var i = 0, len = cs.length; i < len; i++){
32123                 cs[i].render(true);
32124             }
32125         }
32126     },
32127
32128     // private
32129     render : function(bulkRender){
32130         this.ui.render(bulkRender);
32131         if(!this.rendered){
32132             this.rendered = true;
32133             if(this.expanded){
32134                 this.expanded = false;
32135                 this.expand(false, false);
32136             }
32137         }
32138     },
32139
32140     // private
32141     renderIndent : function(deep, refresh){
32142         if(refresh){
32143             this.ui.childIndent = null;
32144         }
32145         this.ui.renderIndent();
32146         if(deep === true && this.childrenRendered){
32147             var cs = this.childNodes;
32148             for(var i = 0, len = cs.length; i < len; i++){
32149                 cs[i].renderIndent(true, refresh);
32150             }
32151         }
32152     }
32153 });/*
32154  * Based on:
32155  * Ext JS Library 1.1.1
32156  * Copyright(c) 2006-2007, Ext JS, LLC.
32157  *
32158  * Originally Released Under LGPL - original licence link has changed is not relivant.
32159  *
32160  * Fork - LGPL
32161  * <script type="text/javascript">
32162  */
32163  
32164 /**
32165  * @class Roo.tree.AsyncTreeNode
32166  * @extends Roo.tree.TreeNode
32167  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
32168  * @constructor
32169  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
32170  */
32171  Roo.tree.AsyncTreeNode = function(config){
32172     this.loaded = false;
32173     this.loading = false;
32174     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
32175     /**
32176     * @event beforeload
32177     * Fires before this node is loaded, return false to cancel
32178     * @param {Node} this This node
32179     */
32180     this.addEvents({'beforeload':true, 'load': true});
32181     /**
32182     * @event load
32183     * Fires when this node is loaded
32184     * @param {Node} this This node
32185     */
32186     /**
32187      * The loader used by this node (defaults to using the tree's defined loader)
32188      * @type TreeLoader
32189      * @property loader
32190      */
32191 };
32192 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
32193     expand : function(deep, anim, callback){
32194         if(this.loading){ // if an async load is already running, waiting til it's done
32195             var timer;
32196             var f = function(){
32197                 if(!this.loading){ // done loading
32198                     clearInterval(timer);
32199                     this.expand(deep, anim, callback);
32200                 }
32201             }.createDelegate(this);
32202             timer = setInterval(f, 200);
32203             return;
32204         }
32205         if(!this.loaded){
32206             if(this.fireEvent("beforeload", this) === false){
32207                 return;
32208             }
32209             this.loading = true;
32210             this.ui.beforeLoad(this);
32211             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
32212             if(loader){
32213                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
32214                 return;
32215             }
32216         }
32217         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
32218     },
32219     
32220     /**
32221      * Returns true if this node is currently loading
32222      * @return {Boolean}
32223      */
32224     isLoading : function(){
32225         return this.loading;  
32226     },
32227     
32228     loadComplete : function(deep, anim, callback){
32229         this.loading = false;
32230         this.loaded = true;
32231         this.ui.afterLoad(this);
32232         this.fireEvent("load", this);
32233         this.expand(deep, anim, callback);
32234     },
32235     
32236     /**
32237      * Returns true if this node has been loaded
32238      * @return {Boolean}
32239      */
32240     isLoaded : function(){
32241         return this.loaded;
32242     },
32243     
32244     hasChildNodes : function(){
32245         if(!this.isLeaf() && !this.loaded){
32246             return true;
32247         }else{
32248             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
32249         }
32250     },
32251
32252     /**
32253      * Trigger a reload for this node
32254      * @param {Function} callback
32255      */
32256     reload : function(callback){
32257         this.collapse(false, false);
32258         while(this.firstChild){
32259             this.removeChild(this.firstChild);
32260         }
32261         this.childrenRendered = false;
32262         this.loaded = false;
32263         if(this.isHiddenRoot()){
32264             this.expanded = false;
32265         }
32266         this.expand(false, false, callback);
32267     }
32268 });/*
32269  * Based on:
32270  * Ext JS Library 1.1.1
32271  * Copyright(c) 2006-2007, Ext JS, LLC.
32272  *
32273  * Originally Released Under LGPL - original licence link has changed is not relivant.
32274  *
32275  * Fork - LGPL
32276  * <script type="text/javascript">
32277  */
32278  
32279 /**
32280  * @class Roo.tree.TreeNodeUI
32281  * @constructor
32282  * @param {Object} node The node to render
32283  * The TreeNode UI implementation is separate from the
32284  * tree implementation. Unless you are customizing the tree UI,
32285  * you should never have to use this directly.
32286  */
32287 Roo.tree.TreeNodeUI = function(node){
32288     this.node = node;
32289     this.rendered = false;
32290     this.animating = false;
32291     this.emptyIcon = Roo.BLANK_IMAGE_URL;
32292 };
32293
32294 Roo.tree.TreeNodeUI.prototype = {
32295     removeChild : function(node){
32296         if(this.rendered){
32297             this.ctNode.removeChild(node.ui.getEl());
32298         }
32299     },
32300
32301     beforeLoad : function(){
32302          this.addClass("x-tree-node-loading");
32303     },
32304
32305     afterLoad : function(){
32306          this.removeClass("x-tree-node-loading");
32307     },
32308
32309     onTextChange : function(node, text, oldText){
32310         if(this.rendered){
32311             this.textNode.innerHTML = text;
32312         }
32313     },
32314
32315     onDisableChange : function(node, state){
32316         this.disabled = state;
32317         if(state){
32318             this.addClass("x-tree-node-disabled");
32319         }else{
32320             this.removeClass("x-tree-node-disabled");
32321         }
32322     },
32323
32324     onSelectedChange : function(state){
32325         if(state){
32326             this.focus();
32327             this.addClass("x-tree-selected");
32328         }else{
32329             //this.blur();
32330             this.removeClass("x-tree-selected");
32331         }
32332     },
32333
32334     onMove : function(tree, node, oldParent, newParent, index, refNode){
32335         this.childIndent = null;
32336         if(this.rendered){
32337             var targetNode = newParent.ui.getContainer();
32338             if(!targetNode){//target not rendered
32339                 this.holder = document.createElement("div");
32340                 this.holder.appendChild(this.wrap);
32341                 return;
32342             }
32343             var insertBefore = refNode ? refNode.ui.getEl() : null;
32344             if(insertBefore){
32345                 targetNode.insertBefore(this.wrap, insertBefore);
32346             }else{
32347                 targetNode.appendChild(this.wrap);
32348             }
32349             this.node.renderIndent(true);
32350         }
32351     },
32352
32353     addClass : function(cls){
32354         if(this.elNode){
32355             Roo.fly(this.elNode).addClass(cls);
32356         }
32357     },
32358
32359     removeClass : function(cls){
32360         if(this.elNode){
32361             Roo.fly(this.elNode).removeClass(cls);
32362         }
32363     },
32364
32365     remove : function(){
32366         if(this.rendered){
32367             this.holder = document.createElement("div");
32368             this.holder.appendChild(this.wrap);
32369         }
32370     },
32371
32372     fireEvent : function(){
32373         return this.node.fireEvent.apply(this.node, arguments);
32374     },
32375
32376     initEvents : function(){
32377         this.node.on("move", this.onMove, this);
32378         var E = Roo.EventManager;
32379         var a = this.anchor;
32380
32381         var el = Roo.fly(a, '_treeui');
32382
32383         if(Roo.isOpera){ // opera render bug ignores the CSS
32384             el.setStyle("text-decoration", "none");
32385         }
32386
32387         el.on("click", this.onClick, this);
32388         el.on("dblclick", this.onDblClick, this);
32389
32390         if(this.checkbox){
32391             Roo.EventManager.on(this.checkbox,
32392                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
32393         }
32394
32395         el.on("contextmenu", this.onContextMenu, this);
32396
32397         var icon = Roo.fly(this.iconNode);
32398         icon.on("click", this.onClick, this);
32399         icon.on("dblclick", this.onDblClick, this);
32400         icon.on("contextmenu", this.onContextMenu, this);
32401         E.on(this.ecNode, "click", this.ecClick, this, true);
32402
32403         if(this.node.disabled){
32404             this.addClass("x-tree-node-disabled");
32405         }
32406         if(this.node.hidden){
32407             this.addClass("x-tree-node-disabled");
32408         }
32409         var ot = this.node.getOwnerTree();
32410         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
32411         if(dd && (!this.node.isRoot || ot.rootVisible)){
32412             Roo.dd.Registry.register(this.elNode, {
32413                 node: this.node,
32414                 handles: this.getDDHandles(),
32415                 isHandle: false
32416             });
32417         }
32418     },
32419
32420     getDDHandles : function(){
32421         return [this.iconNode, this.textNode];
32422     },
32423
32424     hide : function(){
32425         if(this.rendered){
32426             this.wrap.style.display = "none";
32427         }
32428     },
32429
32430     show : function(){
32431         if(this.rendered){
32432             this.wrap.style.display = "";
32433         }
32434     },
32435
32436     onContextMenu : function(e){
32437         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
32438             e.preventDefault();
32439             this.focus();
32440             this.fireEvent("contextmenu", this.node, e);
32441         }
32442     },
32443
32444     onClick : function(e){
32445         if(this.dropping){
32446             e.stopEvent();
32447             return;
32448         }
32449         if(this.fireEvent("beforeclick", this.node, e) !== false){
32450             if(!this.disabled && this.node.attributes.href){
32451                 this.fireEvent("click", this.node, e);
32452                 return;
32453             }
32454             e.preventDefault();
32455             if(this.disabled){
32456                 return;
32457             }
32458
32459             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
32460                 this.node.toggle();
32461             }
32462
32463             this.fireEvent("click", this.node, e);
32464         }else{
32465             e.stopEvent();
32466         }
32467     },
32468
32469     onDblClick : function(e){
32470         e.preventDefault();
32471         if(this.disabled){
32472             return;
32473         }
32474         if(this.checkbox){
32475             this.toggleCheck();
32476         }
32477         if(!this.animating && this.node.hasChildNodes()){
32478             this.node.toggle();
32479         }
32480         this.fireEvent("dblclick", this.node, e);
32481     },
32482
32483     onCheckChange : function(){
32484         var checked = this.checkbox.checked;
32485         this.node.attributes.checked = checked;
32486         this.fireEvent('checkchange', this.node, checked);
32487     },
32488
32489     ecClick : function(e){
32490         if(!this.animating && this.node.hasChildNodes()){
32491             this.node.toggle();
32492         }
32493     },
32494
32495     startDrop : function(){
32496         this.dropping = true;
32497     },
32498
32499     // delayed drop so the click event doesn't get fired on a drop
32500     endDrop : function(){
32501        setTimeout(function(){
32502            this.dropping = false;
32503        }.createDelegate(this), 50);
32504     },
32505
32506     expand : function(){
32507         this.updateExpandIcon();
32508         this.ctNode.style.display = "";
32509     },
32510
32511     focus : function(){
32512         if(!this.node.preventHScroll){
32513             try{this.anchor.focus();
32514             }catch(e){}
32515         }else if(!Roo.isIE){
32516             try{
32517                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
32518                 var l = noscroll.scrollLeft;
32519                 this.anchor.focus();
32520                 noscroll.scrollLeft = l;
32521             }catch(e){}
32522         }
32523     },
32524
32525     toggleCheck : function(value){
32526         var cb = this.checkbox;
32527         if(cb){
32528             cb.checked = (value === undefined ? !cb.checked : value);
32529         }
32530     },
32531
32532     blur : function(){
32533         try{
32534             this.anchor.blur();
32535         }catch(e){}
32536     },
32537
32538     animExpand : function(callback){
32539         var ct = Roo.get(this.ctNode);
32540         ct.stopFx();
32541         if(!this.node.hasChildNodes()){
32542             this.updateExpandIcon();
32543             this.ctNode.style.display = "";
32544             Roo.callback(callback);
32545             return;
32546         }
32547         this.animating = true;
32548         this.updateExpandIcon();
32549
32550         ct.slideIn('t', {
32551            callback : function(){
32552                this.animating = false;
32553                Roo.callback(callback);
32554             },
32555             scope: this,
32556             duration: this.node.ownerTree.duration || .25
32557         });
32558     },
32559
32560     highlight : function(){
32561         var tree = this.node.getOwnerTree();
32562         Roo.fly(this.wrap).highlight(
32563             tree.hlColor || "C3DAF9",
32564             {endColor: tree.hlBaseColor}
32565         );
32566     },
32567
32568     collapse : function(){
32569         this.updateExpandIcon();
32570         this.ctNode.style.display = "none";
32571     },
32572
32573     animCollapse : function(callback){
32574         var ct = Roo.get(this.ctNode);
32575         ct.enableDisplayMode('block');
32576         ct.stopFx();
32577
32578         this.animating = true;
32579         this.updateExpandIcon();
32580
32581         ct.slideOut('t', {
32582             callback : function(){
32583                this.animating = false;
32584                Roo.callback(callback);
32585             },
32586             scope: this,
32587             duration: this.node.ownerTree.duration || .25
32588         });
32589     },
32590
32591     getContainer : function(){
32592         return this.ctNode;
32593     },
32594
32595     getEl : function(){
32596         return this.wrap;
32597     },
32598
32599     appendDDGhost : function(ghostNode){
32600         ghostNode.appendChild(this.elNode.cloneNode(true));
32601     },
32602
32603     getDDRepairXY : function(){
32604         return Roo.lib.Dom.getXY(this.iconNode);
32605     },
32606
32607     onRender : function(){
32608         this.render();
32609     },
32610
32611     render : function(bulkRender){
32612         var n = this.node, a = n.attributes;
32613         var targetNode = n.parentNode ?
32614               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
32615
32616         if(!this.rendered){
32617             this.rendered = true;
32618
32619             this.renderElements(n, a, targetNode, bulkRender);
32620
32621             if(a.qtip){
32622                if(this.textNode.setAttributeNS){
32623                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
32624                    if(a.qtipTitle){
32625                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
32626                    }
32627                }else{
32628                    this.textNode.setAttribute("ext:qtip", a.qtip);
32629                    if(a.qtipTitle){
32630                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
32631                    }
32632                }
32633             }else if(a.qtipCfg){
32634                 a.qtipCfg.target = Roo.id(this.textNode);
32635                 Roo.QuickTips.register(a.qtipCfg);
32636             }
32637             this.initEvents();
32638             if(!this.node.expanded){
32639                 this.updateExpandIcon();
32640             }
32641         }else{
32642             if(bulkRender === true) {
32643                 targetNode.appendChild(this.wrap);
32644             }
32645         }
32646     },
32647
32648     renderElements : function(n, a, targetNode, bulkRender)
32649     {
32650         // add some indent caching, this helps performance when rendering a large tree
32651         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
32652         var t = n.getOwnerTree();
32653         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
32654         if (typeof(n.attributes.html) != 'undefined') {
32655             txt = n.attributes.html;
32656         }
32657         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
32658         var cb = typeof a.checked == 'boolean';
32659         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
32660         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
32661             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
32662             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
32663             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
32664             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
32665             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
32666              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
32667                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
32668             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
32669             "</li>"];
32670
32671         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
32672             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
32673                                 n.nextSibling.ui.getEl(), buf.join(""));
32674         }else{
32675             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
32676         }
32677
32678         this.elNode = this.wrap.childNodes[0];
32679         this.ctNode = this.wrap.childNodes[1];
32680         var cs = this.elNode.childNodes;
32681         this.indentNode = cs[0];
32682         this.ecNode = cs[1];
32683         this.iconNode = cs[2];
32684         var index = 3;
32685         if(cb){
32686             this.checkbox = cs[3];
32687             index++;
32688         }
32689         this.anchor = cs[index];
32690         this.textNode = cs[index].firstChild;
32691     },
32692
32693     getAnchor : function(){
32694         return this.anchor;
32695     },
32696
32697     getTextEl : function(){
32698         return this.textNode;
32699     },
32700
32701     getIconEl : function(){
32702         return this.iconNode;
32703     },
32704
32705     isChecked : function(){
32706         return this.checkbox ? this.checkbox.checked : false;
32707     },
32708
32709     updateExpandIcon : function(){
32710         if(this.rendered){
32711             var n = this.node, c1, c2;
32712             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
32713             var hasChild = n.hasChildNodes();
32714             if(hasChild){
32715                 if(n.expanded){
32716                     cls += "-minus";
32717                     c1 = "x-tree-node-collapsed";
32718                     c2 = "x-tree-node-expanded";
32719                 }else{
32720                     cls += "-plus";
32721                     c1 = "x-tree-node-expanded";
32722                     c2 = "x-tree-node-collapsed";
32723                 }
32724                 if(this.wasLeaf){
32725                     this.removeClass("x-tree-node-leaf");
32726                     this.wasLeaf = false;
32727                 }
32728                 if(this.c1 != c1 || this.c2 != c2){
32729                     Roo.fly(this.elNode).replaceClass(c1, c2);
32730                     this.c1 = c1; this.c2 = c2;
32731                 }
32732             }else{
32733                 // this changes non-leafs into leafs if they have no children.
32734                 // it's not very rational behaviour..
32735                 
32736                 if(!this.wasLeaf && this.node.leaf){
32737                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
32738                     delete this.c1;
32739                     delete this.c2;
32740                     this.wasLeaf = true;
32741                 }
32742             }
32743             var ecc = "x-tree-ec-icon "+cls;
32744             if(this.ecc != ecc){
32745                 this.ecNode.className = ecc;
32746                 this.ecc = ecc;
32747             }
32748         }
32749     },
32750
32751     getChildIndent : function(){
32752         if(!this.childIndent){
32753             var buf = [];
32754             var p = this.node;
32755             while(p){
32756                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
32757                     if(!p.isLast()) {
32758                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
32759                     } else {
32760                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
32761                     }
32762                 }
32763                 p = p.parentNode;
32764             }
32765             this.childIndent = buf.join("");
32766         }
32767         return this.childIndent;
32768     },
32769
32770     renderIndent : function(){
32771         if(this.rendered){
32772             var indent = "";
32773             var p = this.node.parentNode;
32774             if(p){
32775                 indent = p.ui.getChildIndent();
32776             }
32777             if(this.indentMarkup != indent){ // don't rerender if not required
32778                 this.indentNode.innerHTML = indent;
32779                 this.indentMarkup = indent;
32780             }
32781             this.updateExpandIcon();
32782         }
32783     }
32784 };
32785
32786 Roo.tree.RootTreeNodeUI = function(){
32787     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
32788 };
32789 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
32790     render : function(){
32791         if(!this.rendered){
32792             var targetNode = this.node.ownerTree.innerCt.dom;
32793             this.node.expanded = true;
32794             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
32795             this.wrap = this.ctNode = targetNode.firstChild;
32796         }
32797     },
32798     collapse : function(){
32799     },
32800     expand : function(){
32801     }
32802 });/*
32803  * Based on:
32804  * Ext JS Library 1.1.1
32805  * Copyright(c) 2006-2007, Ext JS, LLC.
32806  *
32807  * Originally Released Under LGPL - original licence link has changed is not relivant.
32808  *
32809  * Fork - LGPL
32810  * <script type="text/javascript">
32811  */
32812 /**
32813  * @class Roo.tree.TreeLoader
32814  * @extends Roo.util.Observable
32815  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
32816  * nodes from a specified URL. The response must be a javascript Array definition
32817  * who's elements are node definition objects. eg:
32818  * <pre><code>
32819    [{ 'id': 1, 'text': 'A folder Node', 'leaf': false },
32820     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }]
32821 </code></pre>
32822  * <br><br>
32823  * A server request is sent, and child nodes are loaded only when a node is expanded.
32824  * The loading node's id is passed to the server under the parameter name "node" to
32825  * enable the server to produce the correct child nodes.
32826  * <br><br>
32827  * To pass extra parameters, an event handler may be attached to the "beforeload"
32828  * event, and the parameters specified in the TreeLoader's baseParams property:
32829  * <pre><code>
32830     myTreeLoader.on("beforeload", function(treeLoader, node) {
32831         this.baseParams.category = node.attributes.category;
32832     }, this);
32833 </code></pre><
32834  * This would pass an HTTP parameter called "category" to the server containing
32835  * the value of the Node's "category" attribute.
32836  * @constructor
32837  * Creates a new Treeloader.
32838  * @param {Object} config A config object containing config properties.
32839  */
32840 Roo.tree.TreeLoader = function(config){
32841     this.baseParams = {};
32842     this.requestMethod = "POST";
32843     Roo.apply(this, config);
32844
32845     this.addEvents({
32846     
32847         /**
32848          * @event beforeload
32849          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
32850          * @param {Object} This TreeLoader object.
32851          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32852          * @param {Object} callback The callback function specified in the {@link #load} call.
32853          */
32854         beforeload : true,
32855         /**
32856          * @event load
32857          * Fires when the node has been successfuly loaded.
32858          * @param {Object} This TreeLoader object.
32859          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32860          * @param {Object} response The response object containing the data from the server.
32861          */
32862         load : true,
32863         /**
32864          * @event loadexception
32865          * Fires if the network request failed.
32866          * @param {Object} This TreeLoader object.
32867          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32868          * @param {Object} response The response object containing the data from the server.
32869          */
32870         loadexception : true,
32871         /**
32872          * @event create
32873          * Fires before a node is created, enabling you to return custom Node types 
32874          * @param {Object} This TreeLoader object.
32875          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
32876          */
32877         create : true
32878     });
32879
32880     Roo.tree.TreeLoader.superclass.constructor.call(this);
32881 };
32882
32883 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
32884     /**
32885     * @cfg {String} dataUrl The URL from which to request a Json string which
32886     * specifies an array of node definition object representing the child nodes
32887     * to be loaded.
32888     */
32889     /**
32890     * @cfg {Object} baseParams (optional) An object containing properties which
32891     * specify HTTP parameters to be passed to each request for child nodes.
32892     */
32893     /**
32894     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
32895     * created by this loader. If the attributes sent by the server have an attribute in this object,
32896     * they take priority.
32897     */
32898     /**
32899     * @cfg {Object} uiProviders (optional) An object containing properties which
32900     * 
32901     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
32902     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
32903     * <i>uiProvider</i> attribute of a returned child node is a string rather
32904     * than a reference to a TreeNodeUI implementation, this that string value
32905     * is used as a property name in the uiProviders object. You can define the provider named
32906     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
32907     */
32908     uiProviders : {},
32909
32910     /**
32911     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
32912     * child nodes before loading.
32913     */
32914     clearOnLoad : true,
32915
32916     /**
32917     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
32918     * property on loading, rather than expecting an array. (eg. more compatible to a standard
32919     * Grid query { data : [ .....] }
32920     */
32921     
32922     root : false,
32923      /**
32924     * @cfg {String} queryParam (optional) 
32925     * Name of the query as it will be passed on the querystring (defaults to 'node')
32926     * eg. the request will be ?node=[id]
32927     */
32928     
32929     
32930     queryParam: false,
32931     
32932     /**
32933      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
32934      * This is called automatically when a node is expanded, but may be used to reload
32935      * a node (or append new children if the {@link #clearOnLoad} option is false.)
32936      * @param {Roo.tree.TreeNode} node
32937      * @param {Function} callback
32938      */
32939     load : function(node, callback){
32940         if(this.clearOnLoad){
32941             while(node.firstChild){
32942                 node.removeChild(node.firstChild);
32943             }
32944         }
32945         if(node.attributes.children){ // preloaded json children
32946             var cs = node.attributes.children;
32947             for(var i = 0, len = cs.length; i < len; i++){
32948                 node.appendChild(this.createNode(cs[i]));
32949             }
32950             if(typeof callback == "function"){
32951                 callback();
32952             }
32953         }else if(this.dataUrl){
32954             this.requestData(node, callback);
32955         }
32956     },
32957
32958     getParams: function(node){
32959         var buf = [], bp = this.baseParams;
32960         for(var key in bp){
32961             if(typeof bp[key] != "function"){
32962                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
32963             }
32964         }
32965         var n = this.queryParam === false ? 'node' : this.queryParam;
32966         buf.push(n + "=", encodeURIComponent(node.id));
32967         return buf.join("");
32968     },
32969
32970     requestData : function(node, callback){
32971         if(this.fireEvent("beforeload", this, node, callback) !== false){
32972             this.transId = Roo.Ajax.request({
32973                 method:this.requestMethod,
32974                 url: this.dataUrl||this.url,
32975                 success: this.handleResponse,
32976                 failure: this.handleFailure,
32977                 scope: this,
32978                 argument: {callback: callback, node: node},
32979                 params: this.getParams(node)
32980             });
32981         }else{
32982             // if the load is cancelled, make sure we notify
32983             // the node that we are done
32984             if(typeof callback == "function"){
32985                 callback();
32986             }
32987         }
32988     },
32989
32990     isLoading : function(){
32991         return this.transId ? true : false;
32992     },
32993
32994     abort : function(){
32995         if(this.isLoading()){
32996             Roo.Ajax.abort(this.transId);
32997         }
32998     },
32999
33000     // private
33001     createNode : function(attr)
33002     {
33003         // apply baseAttrs, nice idea Corey!
33004         if(this.baseAttrs){
33005             Roo.applyIf(attr, this.baseAttrs);
33006         }
33007         if(this.applyLoader !== false){
33008             attr.loader = this;
33009         }
33010         // uiProvider = depreciated..
33011         
33012         if(typeof(attr.uiProvider) == 'string'){
33013            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
33014                 /**  eval:var:attr */ eval(attr.uiProvider);
33015         }
33016         if(typeof(this.uiProviders['default']) != 'undefined') {
33017             attr.uiProvider = this.uiProviders['default'];
33018         }
33019         
33020         this.fireEvent('create', this, attr);
33021         
33022         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
33023         return(attr.leaf ?
33024                         new Roo.tree.TreeNode(attr) :
33025                         new Roo.tree.AsyncTreeNode(attr));
33026     },
33027
33028     processResponse : function(response, node, callback)
33029     {
33030         var json = response.responseText;
33031         try {
33032             
33033             var o = Roo.decode(json);
33034             
33035             if (!o.success) {
33036                 // it's a failure condition.
33037                 var a = response.argument;
33038                 this.fireEvent("loadexception", this, a.node, response);
33039                 Roo.log("Load failed - should have a handler really");
33040                 return;
33041             }
33042             
33043             if (this.root !== false) {
33044                 o = o[this.root];
33045             }
33046             
33047             for(var i = 0, len = o.length; i < len; i++){
33048                 var n = this.createNode(o[i]);
33049                 if(n){
33050                     node.appendChild(n);
33051                 }
33052             }
33053             if(typeof callback == "function"){
33054                 callback(this, node);
33055             }
33056         }catch(e){
33057             this.handleFailure(response);
33058         }
33059     },
33060
33061     handleResponse : function(response){
33062         this.transId = false;
33063         var a = response.argument;
33064         this.processResponse(response, a.node, a.callback);
33065         this.fireEvent("load", this, a.node, response);
33066     },
33067
33068     handleFailure : function(response)
33069     {
33070         // should handle failure better..
33071         this.transId = false;
33072         var a = response.argument;
33073         this.fireEvent("loadexception", this, a.node, response);
33074         if(typeof a.callback == "function"){
33075             a.callback(this, a.node);
33076         }
33077     }
33078 });/*
33079  * Based on:
33080  * Ext JS Library 1.1.1
33081  * Copyright(c) 2006-2007, Ext JS, LLC.
33082  *
33083  * Originally Released Under LGPL - original licence link has changed is not relivant.
33084  *
33085  * Fork - LGPL
33086  * <script type="text/javascript">
33087  */
33088
33089 /**
33090 * @class Roo.tree.TreeFilter
33091 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
33092 * @param {TreePanel} tree
33093 * @param {Object} config (optional)
33094  */
33095 Roo.tree.TreeFilter = function(tree, config){
33096     this.tree = tree;
33097     this.filtered = {};
33098     Roo.apply(this, config);
33099 };
33100
33101 Roo.tree.TreeFilter.prototype = {
33102     clearBlank:false,
33103     reverse:false,
33104     autoClear:false,
33105     remove:false,
33106
33107      /**
33108      * Filter the data by a specific attribute.
33109      * @param {String/RegExp} value Either string that the attribute value
33110      * should start with or a RegExp to test against the attribute
33111      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
33112      * @param {TreeNode} startNode (optional) The node to start the filter at.
33113      */
33114     filter : function(value, attr, startNode){
33115         attr = attr || "text";
33116         var f;
33117         if(typeof value == "string"){
33118             var vlen = value.length;
33119             // auto clear empty filter
33120             if(vlen == 0 && this.clearBlank){
33121                 this.clear();
33122                 return;
33123             }
33124             value = value.toLowerCase();
33125             f = function(n){
33126                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
33127             };
33128         }else if(value.exec){ // regex?
33129             f = function(n){
33130                 return value.test(n.attributes[attr]);
33131             };
33132         }else{
33133             throw 'Illegal filter type, must be string or regex';
33134         }
33135         this.filterBy(f, null, startNode);
33136         },
33137
33138     /**
33139      * Filter by a function. The passed function will be called with each
33140      * node in the tree (or from the startNode). If the function returns true, the node is kept
33141      * otherwise it is filtered. If a node is filtered, its children are also filtered.
33142      * @param {Function} fn The filter function
33143      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
33144      */
33145     filterBy : function(fn, scope, startNode){
33146         startNode = startNode || this.tree.root;
33147         if(this.autoClear){
33148             this.clear();
33149         }
33150         var af = this.filtered, rv = this.reverse;
33151         var f = function(n){
33152             if(n == startNode){
33153                 return true;
33154             }
33155             if(af[n.id]){
33156                 return false;
33157             }
33158             var m = fn.call(scope || n, n);
33159             if(!m || rv){
33160                 af[n.id] = n;
33161                 n.ui.hide();
33162                 return false;
33163             }
33164             return true;
33165         };
33166         startNode.cascade(f);
33167         if(this.remove){
33168            for(var id in af){
33169                if(typeof id != "function"){
33170                    var n = af[id];
33171                    if(n && n.parentNode){
33172                        n.parentNode.removeChild(n);
33173                    }
33174                }
33175            }
33176         }
33177     },
33178
33179     /**
33180      * Clears the current filter. Note: with the "remove" option
33181      * set a filter cannot be cleared.
33182      */
33183     clear : function(){
33184         var t = this.tree;
33185         var af = this.filtered;
33186         for(var id in af){
33187             if(typeof id != "function"){
33188                 var n = af[id];
33189                 if(n){
33190                     n.ui.show();
33191                 }
33192             }
33193         }
33194         this.filtered = {};
33195     }
33196 };
33197 /*
33198  * Based on:
33199  * Ext JS Library 1.1.1
33200  * Copyright(c) 2006-2007, Ext JS, LLC.
33201  *
33202  * Originally Released Under LGPL - original licence link has changed is not relivant.
33203  *
33204  * Fork - LGPL
33205  * <script type="text/javascript">
33206  */
33207  
33208
33209 /**
33210  * @class Roo.tree.TreeSorter
33211  * Provides sorting of nodes in a TreePanel
33212  * 
33213  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
33214  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
33215  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
33216  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
33217  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
33218  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
33219  * @constructor
33220  * @param {TreePanel} tree
33221  * @param {Object} config
33222  */
33223 Roo.tree.TreeSorter = function(tree, config){
33224     Roo.apply(this, config);
33225     tree.on("beforechildrenrendered", this.doSort, this);
33226     tree.on("append", this.updateSort, this);
33227     tree.on("insert", this.updateSort, this);
33228     
33229     var dsc = this.dir && this.dir.toLowerCase() == "desc";
33230     var p = this.property || "text";
33231     var sortType = this.sortType;
33232     var fs = this.folderSort;
33233     var cs = this.caseSensitive === true;
33234     var leafAttr = this.leafAttr || 'leaf';
33235
33236     this.sortFn = function(n1, n2){
33237         if(fs){
33238             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
33239                 return 1;
33240             }
33241             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
33242                 return -1;
33243             }
33244         }
33245         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
33246         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
33247         if(v1 < v2){
33248                         return dsc ? +1 : -1;
33249                 }else if(v1 > v2){
33250                         return dsc ? -1 : +1;
33251         }else{
33252                 return 0;
33253         }
33254     };
33255 };
33256
33257 Roo.tree.TreeSorter.prototype = {
33258     doSort : function(node){
33259         node.sort(this.sortFn);
33260     },
33261     
33262     compareNodes : function(n1, n2){
33263         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
33264     },
33265     
33266     updateSort : function(tree, node){
33267         if(node.childrenRendered){
33268             this.doSort.defer(1, this, [node]);
33269         }
33270     }
33271 };/*
33272  * Based on:
33273  * Ext JS Library 1.1.1
33274  * Copyright(c) 2006-2007, Ext JS, LLC.
33275  *
33276  * Originally Released Under LGPL - original licence link has changed is not relivant.
33277  *
33278  * Fork - LGPL
33279  * <script type="text/javascript">
33280  */
33281
33282 if(Roo.dd.DropZone){
33283     
33284 Roo.tree.TreeDropZone = function(tree, config){
33285     this.allowParentInsert = false;
33286     this.allowContainerDrop = false;
33287     this.appendOnly = false;
33288     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
33289     this.tree = tree;
33290     this.lastInsertClass = "x-tree-no-status";
33291     this.dragOverData = {};
33292 };
33293
33294 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
33295     ddGroup : "TreeDD",
33296     
33297     expandDelay : 1000,
33298     
33299     expandNode : function(node){
33300         if(node.hasChildNodes() && !node.isExpanded()){
33301             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
33302         }
33303     },
33304     
33305     queueExpand : function(node){
33306         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
33307     },
33308     
33309     cancelExpand : function(){
33310         if(this.expandProcId){
33311             clearTimeout(this.expandProcId);
33312             this.expandProcId = false;
33313         }
33314     },
33315     
33316     isValidDropPoint : function(n, pt, dd, e, data){
33317         if(!n || !data){ return false; }
33318         var targetNode = n.node;
33319         var dropNode = data.node;
33320         // default drop rules
33321         if(!(targetNode && targetNode.isTarget && pt)){
33322             return false;
33323         }
33324         if(pt == "append" && targetNode.allowChildren === false){
33325             return false;
33326         }
33327         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
33328             return false;
33329         }
33330         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
33331             return false;
33332         }
33333         // reuse the object
33334         var overEvent = this.dragOverData;
33335         overEvent.tree = this.tree;
33336         overEvent.target = targetNode;
33337         overEvent.data = data;
33338         overEvent.point = pt;
33339         overEvent.source = dd;
33340         overEvent.rawEvent = e;
33341         overEvent.dropNode = dropNode;
33342         overEvent.cancel = false;  
33343         var result = this.tree.fireEvent("nodedragover", overEvent);
33344         return overEvent.cancel === false && result !== false;
33345     },
33346     
33347     getDropPoint : function(e, n, dd){
33348         var tn = n.node;
33349         if(tn.isRoot){
33350             return tn.allowChildren !== false ? "append" : false; // always append for root
33351         }
33352         var dragEl = n.ddel;
33353         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
33354         var y = Roo.lib.Event.getPageY(e);
33355         //var noAppend = tn.allowChildren === false || tn.isLeaf();
33356         
33357         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
33358         var noAppend = tn.allowChildren === false;
33359         if(this.appendOnly || tn.parentNode.allowChildren === false){
33360             return noAppend ? false : "append";
33361         }
33362         var noBelow = false;
33363         if(!this.allowParentInsert){
33364             noBelow = tn.hasChildNodes() && tn.isExpanded();
33365         }
33366         var q = (b - t) / (noAppend ? 2 : 3);
33367         if(y >= t && y < (t + q)){
33368             return "above";
33369         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
33370             return "below";
33371         }else{
33372             return "append";
33373         }
33374     },
33375     
33376     onNodeEnter : function(n, dd, e, data){
33377         this.cancelExpand();
33378     },
33379     
33380     onNodeOver : function(n, dd, e, data){
33381         var pt = this.getDropPoint(e, n, dd);
33382         var node = n.node;
33383         
33384         // auto node expand check
33385         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
33386             this.queueExpand(node);
33387         }else if(pt != "append"){
33388             this.cancelExpand();
33389         }
33390         
33391         // set the insert point style on the target node
33392         var returnCls = this.dropNotAllowed;
33393         if(this.isValidDropPoint(n, pt, dd, e, data)){
33394            if(pt){
33395                var el = n.ddel;
33396                var cls;
33397                if(pt == "above"){
33398                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
33399                    cls = "x-tree-drag-insert-above";
33400                }else if(pt == "below"){
33401                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
33402                    cls = "x-tree-drag-insert-below";
33403                }else{
33404                    returnCls = "x-tree-drop-ok-append";
33405                    cls = "x-tree-drag-append";
33406                }
33407                if(this.lastInsertClass != cls){
33408                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
33409                    this.lastInsertClass = cls;
33410                }
33411            }
33412        }
33413        return returnCls;
33414     },
33415     
33416     onNodeOut : function(n, dd, e, data){
33417         this.cancelExpand();
33418         this.removeDropIndicators(n);
33419     },
33420     
33421     onNodeDrop : function(n, dd, e, data){
33422         var point = this.getDropPoint(e, n, dd);
33423         var targetNode = n.node;
33424         targetNode.ui.startDrop();
33425         if(!this.isValidDropPoint(n, point, dd, e, data)){
33426             targetNode.ui.endDrop();
33427             return false;
33428         }
33429         // first try to find the drop node
33430         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
33431         var dropEvent = {
33432             tree : this.tree,
33433             target: targetNode,
33434             data: data,
33435             point: point,
33436             source: dd,
33437             rawEvent: e,
33438             dropNode: dropNode,
33439             cancel: !dropNode   
33440         };
33441         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
33442         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
33443             targetNode.ui.endDrop();
33444             return false;
33445         }
33446         // allow target changing
33447         targetNode = dropEvent.target;
33448         if(point == "append" && !targetNode.isExpanded()){
33449             targetNode.expand(false, null, function(){
33450                 this.completeDrop(dropEvent);
33451             }.createDelegate(this));
33452         }else{
33453             this.completeDrop(dropEvent);
33454         }
33455         return true;
33456     },
33457     
33458     completeDrop : function(de){
33459         var ns = de.dropNode, p = de.point, t = de.target;
33460         if(!(ns instanceof Array)){
33461             ns = [ns];
33462         }
33463         var n;
33464         for(var i = 0, len = ns.length; i < len; i++){
33465             n = ns[i];
33466             if(p == "above"){
33467                 t.parentNode.insertBefore(n, t);
33468             }else if(p == "below"){
33469                 t.parentNode.insertBefore(n, t.nextSibling);
33470             }else{
33471                 t.appendChild(n);
33472             }
33473         }
33474         n.ui.focus();
33475         if(this.tree.hlDrop){
33476             n.ui.highlight();
33477         }
33478         t.ui.endDrop();
33479         this.tree.fireEvent("nodedrop", de);
33480     },
33481     
33482     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
33483         if(this.tree.hlDrop){
33484             dropNode.ui.focus();
33485             dropNode.ui.highlight();
33486         }
33487         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
33488     },
33489     
33490     getTree : function(){
33491         return this.tree;
33492     },
33493     
33494     removeDropIndicators : function(n){
33495         if(n && n.ddel){
33496             var el = n.ddel;
33497             Roo.fly(el).removeClass([
33498                     "x-tree-drag-insert-above",
33499                     "x-tree-drag-insert-below",
33500                     "x-tree-drag-append"]);
33501             this.lastInsertClass = "_noclass";
33502         }
33503     },
33504     
33505     beforeDragDrop : function(target, e, id){
33506         this.cancelExpand();
33507         return true;
33508     },
33509     
33510     afterRepair : function(data){
33511         if(data && Roo.enableFx){
33512             data.node.ui.highlight();
33513         }
33514         this.hideProxy();
33515     }    
33516 });
33517
33518 }
33519 /*
33520  * Based on:
33521  * Ext JS Library 1.1.1
33522  * Copyright(c) 2006-2007, Ext JS, LLC.
33523  *
33524  * Originally Released Under LGPL - original licence link has changed is not relivant.
33525  *
33526  * Fork - LGPL
33527  * <script type="text/javascript">
33528  */
33529  
33530
33531 if(Roo.dd.DragZone){
33532 Roo.tree.TreeDragZone = function(tree, config){
33533     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
33534     this.tree = tree;
33535 };
33536
33537 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
33538     ddGroup : "TreeDD",
33539     
33540     onBeforeDrag : function(data, e){
33541         var n = data.node;
33542         return n && n.draggable && !n.disabled;
33543     },
33544     
33545     onInitDrag : function(e){
33546         var data = this.dragData;
33547         this.tree.getSelectionModel().select(data.node);
33548         this.proxy.update("");
33549         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
33550         this.tree.fireEvent("startdrag", this.tree, data.node, e);
33551     },
33552     
33553     getRepairXY : function(e, data){
33554         return data.node.ui.getDDRepairXY();
33555     },
33556     
33557     onEndDrag : function(data, e){
33558         this.tree.fireEvent("enddrag", this.tree, data.node, e);
33559     },
33560     
33561     onValidDrop : function(dd, e, id){
33562         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
33563         this.hideProxy();
33564     },
33565     
33566     beforeInvalidDrop : function(e, id){
33567         // this scrolls the original position back into view
33568         var sm = this.tree.getSelectionModel();
33569         sm.clearSelections();
33570         sm.select(this.dragData.node);
33571     }
33572 });
33573 }/*
33574  * Based on:
33575  * Ext JS Library 1.1.1
33576  * Copyright(c) 2006-2007, Ext JS, LLC.
33577  *
33578  * Originally Released Under LGPL - original licence link has changed is not relivant.
33579  *
33580  * Fork - LGPL
33581  * <script type="text/javascript">
33582  */
33583 /**
33584  * @class Roo.tree.TreeEditor
33585  * @extends Roo.Editor
33586  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
33587  * as the editor field.
33588  * @constructor
33589  * @param {Object} config (used to be the tree panel.)
33590  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
33591  * 
33592  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
33593  * @cfg {Roo.form.TextField|Object} field The field configuration
33594  *
33595  * 
33596  */
33597 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
33598     var tree = config;
33599     var field;
33600     if (oldconfig) { // old style..
33601         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
33602     } else {
33603         // new style..
33604         tree = config.tree;
33605         config.field = config.field  || {};
33606         config.field.xtype = 'TextField';
33607         field = Roo.factory(config.field, Roo.form);
33608     }
33609     config = config || {};
33610     
33611     
33612     this.addEvents({
33613         /**
33614          * @event beforenodeedit
33615          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
33616          * false from the handler of this event.
33617          * @param {Editor} this
33618          * @param {Roo.tree.Node} node 
33619          */
33620         "beforenodeedit" : true
33621     });
33622     
33623     //Roo.log(config);
33624     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
33625
33626     this.tree = tree;
33627
33628     tree.on('beforeclick', this.beforeNodeClick, this);
33629     tree.getTreeEl().on('mousedown', this.hide, this);
33630     this.on('complete', this.updateNode, this);
33631     this.on('beforestartedit', this.fitToTree, this);
33632     this.on('startedit', this.bindScroll, this, {delay:10});
33633     this.on('specialkey', this.onSpecialKey, this);
33634 };
33635
33636 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
33637     /**
33638      * @cfg {String} alignment
33639      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
33640      */
33641     alignment: "l-l",
33642     // inherit
33643     autoSize: false,
33644     /**
33645      * @cfg {Boolean} hideEl
33646      * True to hide the bound element while the editor is displayed (defaults to false)
33647      */
33648     hideEl : false,
33649     /**
33650      * @cfg {String} cls
33651      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
33652      */
33653     cls: "x-small-editor x-tree-editor",
33654     /**
33655      * @cfg {Boolean} shim
33656      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
33657      */
33658     shim:false,
33659     // inherit
33660     shadow:"frame",
33661     /**
33662      * @cfg {Number} maxWidth
33663      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
33664      * the containing tree element's size, it will be automatically limited for you to the container width, taking
33665      * scroll and client offsets into account prior to each edit.
33666      */
33667     maxWidth: 250,
33668
33669     editDelay : 350,
33670
33671     // private
33672     fitToTree : function(ed, el){
33673         var td = this.tree.getTreeEl().dom, nd = el.dom;
33674         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
33675             td.scrollLeft = nd.offsetLeft;
33676         }
33677         var w = Math.min(
33678                 this.maxWidth,
33679                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
33680         this.setSize(w, '');
33681         
33682         return this.fireEvent('beforenodeedit', this, this.editNode);
33683         
33684     },
33685
33686     // private
33687     triggerEdit : function(node){
33688         this.completeEdit();
33689         this.editNode = node;
33690         this.startEdit(node.ui.textNode, node.text);
33691     },
33692
33693     // private
33694     bindScroll : function(){
33695         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
33696     },
33697
33698     // private
33699     beforeNodeClick : function(node, e){
33700         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
33701         this.lastClick = new Date();
33702         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
33703             e.stopEvent();
33704             this.triggerEdit(node);
33705             return false;
33706         }
33707         return true;
33708     },
33709
33710     // private
33711     updateNode : function(ed, value){
33712         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
33713         this.editNode.setText(value);
33714     },
33715
33716     // private
33717     onHide : function(){
33718         Roo.tree.TreeEditor.superclass.onHide.call(this);
33719         if(this.editNode){
33720             this.editNode.ui.focus();
33721         }
33722     },
33723
33724     // private
33725     onSpecialKey : function(field, e){
33726         var k = e.getKey();
33727         if(k == e.ESC){
33728             e.stopEvent();
33729             this.cancelEdit();
33730         }else if(k == e.ENTER && !e.hasModifier()){
33731             e.stopEvent();
33732             this.completeEdit();
33733         }
33734     }
33735 });//<Script type="text/javascript">
33736 /*
33737  * Based on:
33738  * Ext JS Library 1.1.1
33739  * Copyright(c) 2006-2007, Ext JS, LLC.
33740  *
33741  * Originally Released Under LGPL - original licence link has changed is not relivant.
33742  *
33743  * Fork - LGPL
33744  * <script type="text/javascript">
33745  */
33746  
33747 /**
33748  * Not documented??? - probably should be...
33749  */
33750
33751 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
33752     //focus: Roo.emptyFn, // prevent odd scrolling behavior
33753     
33754     renderElements : function(n, a, targetNode, bulkRender){
33755         //consel.log("renderElements?");
33756         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
33757
33758         var t = n.getOwnerTree();
33759         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
33760         
33761         var cols = t.columns;
33762         var bw = t.borderWidth;
33763         var c = cols[0];
33764         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
33765          var cb = typeof a.checked == "boolean";
33766         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
33767         var colcls = 'x-t-' + tid + '-c0';
33768         var buf = [
33769             '<li class="x-tree-node">',
33770             
33771                 
33772                 '<div class="x-tree-node-el ', a.cls,'">',
33773                     // extran...
33774                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
33775                 
33776                 
33777                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
33778                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
33779                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
33780                            (a.icon ? ' x-tree-node-inline-icon' : ''),
33781                            (a.iconCls ? ' '+a.iconCls : ''),
33782                            '" unselectable="on" />',
33783                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
33784                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
33785                              
33786                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
33787                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
33788                             '<span unselectable="on" qtip="' + tx + '">',
33789                              tx,
33790                              '</span></a>' ,
33791                     '</div>',
33792                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
33793                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
33794                  ];
33795         for(var i = 1, len = cols.length; i < len; i++){
33796             c = cols[i];
33797             colcls = 'x-t-' + tid + '-c' +i;
33798             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
33799             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
33800                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
33801                       "</div>");
33802          }
33803          
33804          buf.push(
33805             '</a>',
33806             '<div class="x-clear"></div></div>',
33807             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
33808             "</li>");
33809         
33810         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
33811             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
33812                                 n.nextSibling.ui.getEl(), buf.join(""));
33813         }else{
33814             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
33815         }
33816         var el = this.wrap.firstChild;
33817         this.elRow = el;
33818         this.elNode = el.firstChild;
33819         this.ranchor = el.childNodes[1];
33820         this.ctNode = this.wrap.childNodes[1];
33821         var cs = el.firstChild.childNodes;
33822         this.indentNode = cs[0];
33823         this.ecNode = cs[1];
33824         this.iconNode = cs[2];
33825         var index = 3;
33826         if(cb){
33827             this.checkbox = cs[3];
33828             index++;
33829         }
33830         this.anchor = cs[index];
33831         
33832         this.textNode = cs[index].firstChild;
33833         
33834         //el.on("click", this.onClick, this);
33835         //el.on("dblclick", this.onDblClick, this);
33836         
33837         
33838        // console.log(this);
33839     },
33840     initEvents : function(){
33841         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
33842         
33843             
33844         var a = this.ranchor;
33845
33846         var el = Roo.get(a);
33847
33848         if(Roo.isOpera){ // opera render bug ignores the CSS
33849             el.setStyle("text-decoration", "none");
33850         }
33851
33852         el.on("click", this.onClick, this);
33853         el.on("dblclick", this.onDblClick, this);
33854         el.on("contextmenu", this.onContextMenu, this);
33855         
33856     },
33857     
33858     /*onSelectedChange : function(state){
33859         if(state){
33860             this.focus();
33861             this.addClass("x-tree-selected");
33862         }else{
33863             //this.blur();
33864             this.removeClass("x-tree-selected");
33865         }
33866     },*/
33867     addClass : function(cls){
33868         if(this.elRow){
33869             Roo.fly(this.elRow).addClass(cls);
33870         }
33871         
33872     },
33873     
33874     
33875     removeClass : function(cls){
33876         if(this.elRow){
33877             Roo.fly(this.elRow).removeClass(cls);
33878         }
33879     }
33880
33881     
33882     
33883 });//<Script type="text/javascript">
33884
33885 /*
33886  * Based on:
33887  * Ext JS Library 1.1.1
33888  * Copyright(c) 2006-2007, Ext JS, LLC.
33889  *
33890  * Originally Released Under LGPL - original licence link has changed is not relivant.
33891  *
33892  * Fork - LGPL
33893  * <script type="text/javascript">
33894  */
33895  
33896
33897 /**
33898  * @class Roo.tree.ColumnTree
33899  * @extends Roo.data.TreePanel
33900  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
33901  * @cfg {int} borderWidth  compined right/left border allowance
33902  * @constructor
33903  * @param {String/HTMLElement/Element} el The container element
33904  * @param {Object} config
33905  */
33906 Roo.tree.ColumnTree =  function(el, config)
33907 {
33908    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
33909    this.addEvents({
33910         /**
33911         * @event resize
33912         * Fire this event on a container when it resizes
33913         * @param {int} w Width
33914         * @param {int} h Height
33915         */
33916        "resize" : true
33917     });
33918     this.on('resize', this.onResize, this);
33919 };
33920
33921 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
33922     //lines:false,
33923     
33924     
33925     borderWidth: Roo.isBorderBox ? 0 : 2, 
33926     headEls : false,
33927     
33928     render : function(){
33929         // add the header.....
33930        
33931         Roo.tree.ColumnTree.superclass.render.apply(this);
33932         
33933         this.el.addClass('x-column-tree');
33934         
33935         this.headers = this.el.createChild(
33936             {cls:'x-tree-headers'},this.innerCt.dom);
33937    
33938         var cols = this.columns, c;
33939         var totalWidth = 0;
33940         this.headEls = [];
33941         var  len = cols.length;
33942         for(var i = 0; i < len; i++){
33943              c = cols[i];
33944              totalWidth += c.width;
33945             this.headEls.push(this.headers.createChild({
33946                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
33947                  cn: {
33948                      cls:'x-tree-hd-text',
33949                      html: c.header
33950                  },
33951                  style:'width:'+(c.width-this.borderWidth)+'px;'
33952              }));
33953         }
33954         this.headers.createChild({cls:'x-clear'});
33955         // prevent floats from wrapping when clipped
33956         this.headers.setWidth(totalWidth);
33957         //this.innerCt.setWidth(totalWidth);
33958         this.innerCt.setStyle({ overflow: 'auto' });
33959         this.onResize(this.width, this.height);
33960              
33961         
33962     },
33963     onResize : function(w,h)
33964     {
33965         this.height = h;
33966         this.width = w;
33967         // resize cols..
33968         this.innerCt.setWidth(this.width);
33969         this.innerCt.setHeight(this.height-20);
33970         
33971         // headers...
33972         var cols = this.columns, c;
33973         var totalWidth = 0;
33974         var expEl = false;
33975         var len = cols.length;
33976         for(var i = 0; i < len; i++){
33977             c = cols[i];
33978             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
33979                 // it's the expander..
33980                 expEl  = this.headEls[i];
33981                 continue;
33982             }
33983             totalWidth += c.width;
33984             
33985         }
33986         if (expEl) {
33987             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
33988         }
33989         this.headers.setWidth(w-20);
33990
33991         
33992         
33993         
33994     }
33995 });
33996 /*
33997  * Based on:
33998  * Ext JS Library 1.1.1
33999  * Copyright(c) 2006-2007, Ext JS, LLC.
34000  *
34001  * Originally Released Under LGPL - original licence link has changed is not relivant.
34002  *
34003  * Fork - LGPL
34004  * <script type="text/javascript">
34005  */
34006  
34007 /**
34008  * @class Roo.menu.Menu
34009  * @extends Roo.util.Observable
34010  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
34011  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
34012  * @constructor
34013  * Creates a new Menu
34014  * @param {Object} config Configuration options
34015  */
34016 Roo.menu.Menu = function(config){
34017     Roo.apply(this, config);
34018     this.id = this.id || Roo.id();
34019     this.addEvents({
34020         /**
34021          * @event beforeshow
34022          * Fires before this menu is displayed
34023          * @param {Roo.menu.Menu} this
34024          */
34025         beforeshow : true,
34026         /**
34027          * @event beforehide
34028          * Fires before this menu is hidden
34029          * @param {Roo.menu.Menu} this
34030          */
34031         beforehide : true,
34032         /**
34033          * @event show
34034          * Fires after this menu is displayed
34035          * @param {Roo.menu.Menu} this
34036          */
34037         show : true,
34038         /**
34039          * @event hide
34040          * Fires after this menu is hidden
34041          * @param {Roo.menu.Menu} this
34042          */
34043         hide : true,
34044         /**
34045          * @event click
34046          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
34047          * @param {Roo.menu.Menu} this
34048          * @param {Roo.menu.Item} menuItem The menu item that was clicked
34049          * @param {Roo.EventObject} e
34050          */
34051         click : true,
34052         /**
34053          * @event mouseover
34054          * Fires when the mouse is hovering over this menu
34055          * @param {Roo.menu.Menu} this
34056          * @param {Roo.EventObject} e
34057          * @param {Roo.menu.Item} menuItem The menu item that was clicked
34058          */
34059         mouseover : true,
34060         /**
34061          * @event mouseout
34062          * Fires when the mouse exits this menu
34063          * @param {Roo.menu.Menu} this
34064          * @param {Roo.EventObject} e
34065          * @param {Roo.menu.Item} menuItem The menu item that was clicked
34066          */
34067         mouseout : true,
34068         /**
34069          * @event itemclick
34070          * Fires when a menu item contained in this menu is clicked
34071          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
34072          * @param {Roo.EventObject} e
34073          */
34074         itemclick: true
34075     });
34076     if (this.registerMenu) {
34077         Roo.menu.MenuMgr.register(this);
34078     }
34079     
34080     var mis = this.items;
34081     this.items = new Roo.util.MixedCollection();
34082     if(mis){
34083         this.add.apply(this, mis);
34084     }
34085 };
34086
34087 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
34088     /**
34089      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
34090      */
34091     minWidth : 120,
34092     /**
34093      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
34094      * for bottom-right shadow (defaults to "sides")
34095      */
34096     shadow : "sides",
34097     /**
34098      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
34099      * this menu (defaults to "tl-tr?")
34100      */
34101     subMenuAlign : "tl-tr?",
34102     /**
34103      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
34104      * relative to its element of origin (defaults to "tl-bl?")
34105      */
34106     defaultAlign : "tl-bl?",
34107     /**
34108      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
34109      */
34110     allowOtherMenus : false,
34111     /**
34112      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
34113      */
34114     registerMenu : true,
34115
34116     hidden:true,
34117
34118     // private
34119     render : function(){
34120         if(this.el){
34121             return;
34122         }
34123         var el = this.el = new Roo.Layer({
34124             cls: "x-menu",
34125             shadow:this.shadow,
34126             constrain: false,
34127             parentEl: this.parentEl || document.body,
34128             zindex:15000
34129         });
34130
34131         this.keyNav = new Roo.menu.MenuNav(this);
34132
34133         if(this.plain){
34134             el.addClass("x-menu-plain");
34135         }
34136         if(this.cls){
34137             el.addClass(this.cls);
34138         }
34139         // generic focus element
34140         this.focusEl = el.createChild({
34141             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
34142         });
34143         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
34144         ul.on("click", this.onClick, this);
34145         ul.on("mouseover", this.onMouseOver, this);
34146         ul.on("mouseout", this.onMouseOut, this);
34147         this.items.each(function(item){
34148             var li = document.createElement("li");
34149             li.className = "x-menu-list-item";
34150             ul.dom.appendChild(li);
34151             item.render(li, this);
34152         }, this);
34153         this.ul = ul;
34154         this.autoWidth();
34155     },
34156
34157     // private
34158     autoWidth : function(){
34159         var el = this.el, ul = this.ul;
34160         if(!el){
34161             return;
34162         }
34163         var w = this.width;
34164         if(w){
34165             el.setWidth(w);
34166         }else if(Roo.isIE){
34167             el.setWidth(this.minWidth);
34168             var t = el.dom.offsetWidth; // force recalc
34169             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
34170         }
34171     },
34172
34173     // private
34174     delayAutoWidth : function(){
34175         if(this.rendered){
34176             if(!this.awTask){
34177                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
34178             }
34179             this.awTask.delay(20);
34180         }
34181     },
34182
34183     // private
34184     findTargetItem : function(e){
34185         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
34186         if(t && t.menuItemId){
34187             return this.items.get(t.menuItemId);
34188         }
34189     },
34190
34191     // private
34192     onClick : function(e){
34193         var t;
34194         if(t = this.findTargetItem(e)){
34195             t.onClick(e);
34196             this.fireEvent("click", this, t, e);
34197         }
34198     },
34199
34200     // private
34201     setActiveItem : function(item, autoExpand){
34202         if(item != this.activeItem){
34203             if(this.activeItem){
34204                 this.activeItem.deactivate();
34205             }
34206             this.activeItem = item;
34207             item.activate(autoExpand);
34208         }else if(autoExpand){
34209             item.expandMenu();
34210         }
34211     },
34212
34213     // private
34214     tryActivate : function(start, step){
34215         var items = this.items;
34216         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
34217             var item = items.get(i);
34218             if(!item.disabled && item.canActivate){
34219                 this.setActiveItem(item, false);
34220                 return item;
34221             }
34222         }
34223         return false;
34224     },
34225
34226     // private
34227     onMouseOver : function(e){
34228         var t;
34229         if(t = this.findTargetItem(e)){
34230             if(t.canActivate && !t.disabled){
34231                 this.setActiveItem(t, true);
34232             }
34233         }
34234         this.fireEvent("mouseover", this, e, t);
34235     },
34236
34237     // private
34238     onMouseOut : function(e){
34239         var t;
34240         if(t = this.findTargetItem(e)){
34241             if(t == this.activeItem && t.shouldDeactivate(e)){
34242                 this.activeItem.deactivate();
34243                 delete this.activeItem;
34244             }
34245         }
34246         this.fireEvent("mouseout", this, e, t);
34247     },
34248
34249     /**
34250      * Read-only.  Returns true if the menu is currently displayed, else false.
34251      * @type Boolean
34252      */
34253     isVisible : function(){
34254         return this.el && !this.hidden;
34255     },
34256
34257     /**
34258      * Displays this menu relative to another element
34259      * @param {String/HTMLElement/Roo.Element} element The element to align to
34260      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
34261      * the element (defaults to this.defaultAlign)
34262      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
34263      */
34264     show : function(el, pos, parentMenu){
34265         this.parentMenu = parentMenu;
34266         if(!this.el){
34267             this.render();
34268         }
34269         this.fireEvent("beforeshow", this);
34270         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
34271     },
34272
34273     /**
34274      * Displays this menu at a specific xy position
34275      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
34276      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
34277      */
34278     showAt : function(xy, parentMenu, /* private: */_e){
34279         this.parentMenu = parentMenu;
34280         if(!this.el){
34281             this.render();
34282         }
34283         if(_e !== false){
34284             this.fireEvent("beforeshow", this);
34285             xy = this.el.adjustForConstraints(xy);
34286         }
34287         this.el.setXY(xy);
34288         this.el.show();
34289         this.hidden = false;
34290         this.focus();
34291         this.fireEvent("show", this);
34292     },
34293
34294     focus : function(){
34295         if(!this.hidden){
34296             this.doFocus.defer(50, this);
34297         }
34298     },
34299
34300     doFocus : function(){
34301         if(!this.hidden){
34302             this.focusEl.focus();
34303         }
34304     },
34305
34306     /**
34307      * Hides this menu and optionally all parent menus
34308      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
34309      */
34310     hide : function(deep){
34311         if(this.el && this.isVisible()){
34312             this.fireEvent("beforehide", this);
34313             if(this.activeItem){
34314                 this.activeItem.deactivate();
34315                 this.activeItem = null;
34316             }
34317             this.el.hide();
34318             this.hidden = true;
34319             this.fireEvent("hide", this);
34320         }
34321         if(deep === true && this.parentMenu){
34322             this.parentMenu.hide(true);
34323         }
34324     },
34325
34326     /**
34327      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
34328      * Any of the following are valid:
34329      * <ul>
34330      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
34331      * <li>An HTMLElement object which will be converted to a menu item</li>
34332      * <li>A menu item config object that will be created as a new menu item</li>
34333      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
34334      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
34335      * </ul>
34336      * Usage:
34337      * <pre><code>
34338 // Create the menu
34339 var menu = new Roo.menu.Menu();
34340
34341 // Create a menu item to add by reference
34342 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
34343
34344 // Add a bunch of items at once using different methods.
34345 // Only the last item added will be returned.
34346 var item = menu.add(
34347     menuItem,                // add existing item by ref
34348     'Dynamic Item',          // new TextItem
34349     '-',                     // new separator
34350     { text: 'Config Item' }  // new item by config
34351 );
34352 </code></pre>
34353      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
34354      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
34355      */
34356     add : function(){
34357         var a = arguments, l = a.length, item;
34358         for(var i = 0; i < l; i++){
34359             var el = a[i];
34360             if ((typeof(el) == "object") && el.xtype && el.xns) {
34361                 el = Roo.factory(el, Roo.menu);
34362             }
34363             
34364             if(el.render){ // some kind of Item
34365                 item = this.addItem(el);
34366             }else if(typeof el == "string"){ // string
34367                 if(el == "separator" || el == "-"){
34368                     item = this.addSeparator();
34369                 }else{
34370                     item = this.addText(el);
34371                 }
34372             }else if(el.tagName || el.el){ // element
34373                 item = this.addElement(el);
34374             }else if(typeof el == "object"){ // must be menu item config?
34375                 item = this.addMenuItem(el);
34376             }
34377         }
34378         return item;
34379     },
34380
34381     /**
34382      * Returns this menu's underlying {@link Roo.Element} object
34383      * @return {Roo.Element} The element
34384      */
34385     getEl : function(){
34386         if(!this.el){
34387             this.render();
34388         }
34389         return this.el;
34390     },
34391
34392     /**
34393      * Adds a separator bar to the menu
34394      * @return {Roo.menu.Item} The menu item that was added
34395      */
34396     addSeparator : function(){
34397         return this.addItem(new Roo.menu.Separator());
34398     },
34399
34400     /**
34401      * Adds an {@link Roo.Element} object to the menu
34402      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
34403      * @return {Roo.menu.Item} The menu item that was added
34404      */
34405     addElement : function(el){
34406         return this.addItem(new Roo.menu.BaseItem(el));
34407     },
34408
34409     /**
34410      * Adds an existing object based on {@link Roo.menu.Item} to the menu
34411      * @param {Roo.menu.Item} item The menu item to add
34412      * @return {Roo.menu.Item} The menu item that was added
34413      */
34414     addItem : function(item){
34415         this.items.add(item);
34416         if(this.ul){
34417             var li = document.createElement("li");
34418             li.className = "x-menu-list-item";
34419             this.ul.dom.appendChild(li);
34420             item.render(li, this);
34421             this.delayAutoWidth();
34422         }
34423         return item;
34424     },
34425
34426     /**
34427      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
34428      * @param {Object} config A MenuItem config object
34429      * @return {Roo.menu.Item} The menu item that was added
34430      */
34431     addMenuItem : function(config){
34432         if(!(config instanceof Roo.menu.Item)){
34433             if(typeof config.checked == "boolean"){ // must be check menu item config?
34434                 config = new Roo.menu.CheckItem(config);
34435             }else{
34436                 config = new Roo.menu.Item(config);
34437             }
34438         }
34439         return this.addItem(config);
34440     },
34441
34442     /**
34443      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
34444      * @param {String} text The text to display in the menu item
34445      * @return {Roo.menu.Item} The menu item that was added
34446      */
34447     addText : function(text){
34448         return this.addItem(new Roo.menu.TextItem({ text : text }));
34449     },
34450
34451     /**
34452      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
34453      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
34454      * @param {Roo.menu.Item} item The menu item to add
34455      * @return {Roo.menu.Item} The menu item that was added
34456      */
34457     insert : function(index, item){
34458         this.items.insert(index, item);
34459         if(this.ul){
34460             var li = document.createElement("li");
34461             li.className = "x-menu-list-item";
34462             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
34463             item.render(li, this);
34464             this.delayAutoWidth();
34465         }
34466         return item;
34467     },
34468
34469     /**
34470      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
34471      * @param {Roo.menu.Item} item The menu item to remove
34472      */
34473     remove : function(item){
34474         this.items.removeKey(item.id);
34475         item.destroy();
34476     },
34477
34478     /**
34479      * Removes and destroys all items in the menu
34480      */
34481     removeAll : function(){
34482         var f;
34483         while(f = this.items.first()){
34484             this.remove(f);
34485         }
34486     }
34487 });
34488
34489 // MenuNav is a private utility class used internally by the Menu
34490 Roo.menu.MenuNav = function(menu){
34491     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
34492     this.scope = this.menu = menu;
34493 };
34494
34495 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
34496     doRelay : function(e, h){
34497         var k = e.getKey();
34498         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
34499             this.menu.tryActivate(0, 1);
34500             return false;
34501         }
34502         return h.call(this.scope || this, e, this.menu);
34503     },
34504
34505     up : function(e, m){
34506         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
34507             m.tryActivate(m.items.length-1, -1);
34508         }
34509     },
34510
34511     down : function(e, m){
34512         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
34513             m.tryActivate(0, 1);
34514         }
34515     },
34516
34517     right : function(e, m){
34518         if(m.activeItem){
34519             m.activeItem.expandMenu(true);
34520         }
34521     },
34522
34523     left : function(e, m){
34524         m.hide();
34525         if(m.parentMenu && m.parentMenu.activeItem){
34526             m.parentMenu.activeItem.activate();
34527         }
34528     },
34529
34530     enter : function(e, m){
34531         if(m.activeItem){
34532             e.stopPropagation();
34533             m.activeItem.onClick(e);
34534             m.fireEvent("click", this, m.activeItem);
34535             return true;
34536         }
34537     }
34538 });/*
34539  * Based on:
34540  * Ext JS Library 1.1.1
34541  * Copyright(c) 2006-2007, Ext JS, LLC.
34542  *
34543  * Originally Released Under LGPL - original licence link has changed is not relivant.
34544  *
34545  * Fork - LGPL
34546  * <script type="text/javascript">
34547  */
34548  
34549 /**
34550  * @class Roo.menu.MenuMgr
34551  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
34552  * @singleton
34553  */
34554 Roo.menu.MenuMgr = function(){
34555    var menus, active, groups = {}, attached = false, lastShow = new Date();
34556
34557    // private - called when first menu is created
34558    function init(){
34559        menus = {};
34560        active = new Roo.util.MixedCollection();
34561        Roo.get(document).addKeyListener(27, function(){
34562            if(active.length > 0){
34563                hideAll();
34564            }
34565        });
34566    }
34567
34568    // private
34569    function hideAll(){
34570        if(active && active.length > 0){
34571            var c = active.clone();
34572            c.each(function(m){
34573                m.hide();
34574            });
34575        }
34576    }
34577
34578    // private
34579    function onHide(m){
34580        active.remove(m);
34581        if(active.length < 1){
34582            Roo.get(document).un("mousedown", onMouseDown);
34583            attached = false;
34584        }
34585    }
34586
34587    // private
34588    function onShow(m){
34589        var last = active.last();
34590        lastShow = new Date();
34591        active.add(m);
34592        if(!attached){
34593            Roo.get(document).on("mousedown", onMouseDown);
34594            attached = true;
34595        }
34596        if(m.parentMenu){
34597           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
34598           m.parentMenu.activeChild = m;
34599        }else if(last && last.isVisible()){
34600           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
34601        }
34602    }
34603
34604    // private
34605    function onBeforeHide(m){
34606        if(m.activeChild){
34607            m.activeChild.hide();
34608        }
34609        if(m.autoHideTimer){
34610            clearTimeout(m.autoHideTimer);
34611            delete m.autoHideTimer;
34612        }
34613    }
34614
34615    // private
34616    function onBeforeShow(m){
34617        var pm = m.parentMenu;
34618        if(!pm && !m.allowOtherMenus){
34619            hideAll();
34620        }else if(pm && pm.activeChild && active != m){
34621            pm.activeChild.hide();
34622        }
34623    }
34624
34625    // private
34626    function onMouseDown(e){
34627        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
34628            hideAll();
34629        }
34630    }
34631
34632    // private
34633    function onBeforeCheck(mi, state){
34634        if(state){
34635            var g = groups[mi.group];
34636            for(var i = 0, l = g.length; i < l; i++){
34637                if(g[i] != mi){
34638                    g[i].setChecked(false);
34639                }
34640            }
34641        }
34642    }
34643
34644    return {
34645
34646        /**
34647         * Hides all menus that are currently visible
34648         */
34649        hideAll : function(){
34650             hideAll();  
34651        },
34652
34653        // private
34654        register : function(menu){
34655            if(!menus){
34656                init();
34657            }
34658            menus[menu.id] = menu;
34659            menu.on("beforehide", onBeforeHide);
34660            menu.on("hide", onHide);
34661            menu.on("beforeshow", onBeforeShow);
34662            menu.on("show", onShow);
34663            var g = menu.group;
34664            if(g && menu.events["checkchange"]){
34665                if(!groups[g]){
34666                    groups[g] = [];
34667                }
34668                groups[g].push(menu);
34669                menu.on("checkchange", onCheck);
34670            }
34671        },
34672
34673         /**
34674          * Returns a {@link Roo.menu.Menu} object
34675          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
34676          * be used to generate and return a new Menu instance.
34677          */
34678        get : function(menu){
34679            if(typeof menu == "string"){ // menu id
34680                return menus[menu];
34681            }else if(menu.events){  // menu instance
34682                return menu;
34683            }else if(typeof menu.length == 'number'){ // array of menu items?
34684                return new Roo.menu.Menu({items:menu});
34685            }else{ // otherwise, must be a config
34686                return new Roo.menu.Menu(menu);
34687            }
34688        },
34689
34690        // private
34691        unregister : function(menu){
34692            delete menus[menu.id];
34693            menu.un("beforehide", onBeforeHide);
34694            menu.un("hide", onHide);
34695            menu.un("beforeshow", onBeforeShow);
34696            menu.un("show", onShow);
34697            var g = menu.group;
34698            if(g && menu.events["checkchange"]){
34699                groups[g].remove(menu);
34700                menu.un("checkchange", onCheck);
34701            }
34702        },
34703
34704        // private
34705        registerCheckable : function(menuItem){
34706            var g = menuItem.group;
34707            if(g){
34708                if(!groups[g]){
34709                    groups[g] = [];
34710                }
34711                groups[g].push(menuItem);
34712                menuItem.on("beforecheckchange", onBeforeCheck);
34713            }
34714        },
34715
34716        // private
34717        unregisterCheckable : function(menuItem){
34718            var g = menuItem.group;
34719            if(g){
34720                groups[g].remove(menuItem);
34721                menuItem.un("beforecheckchange", onBeforeCheck);
34722            }
34723        }
34724    };
34725 }();/*
34726  * Based on:
34727  * Ext JS Library 1.1.1
34728  * Copyright(c) 2006-2007, Ext JS, LLC.
34729  *
34730  * Originally Released Under LGPL - original licence link has changed is not relivant.
34731  *
34732  * Fork - LGPL
34733  * <script type="text/javascript">
34734  */
34735  
34736
34737 /**
34738  * @class Roo.menu.BaseItem
34739  * @extends Roo.Component
34740  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
34741  * management and base configuration options shared by all menu components.
34742  * @constructor
34743  * Creates a new BaseItem
34744  * @param {Object} config Configuration options
34745  */
34746 Roo.menu.BaseItem = function(config){
34747     Roo.menu.BaseItem.superclass.constructor.call(this, config);
34748
34749     this.addEvents({
34750         /**
34751          * @event click
34752          * Fires when this item is clicked
34753          * @param {Roo.menu.BaseItem} this
34754          * @param {Roo.EventObject} e
34755          */
34756         click: true,
34757         /**
34758          * @event activate
34759          * Fires when this item is activated
34760          * @param {Roo.menu.BaseItem} this
34761          */
34762         activate : true,
34763         /**
34764          * @event deactivate
34765          * Fires when this item is deactivated
34766          * @param {Roo.menu.BaseItem} this
34767          */
34768         deactivate : true
34769     });
34770
34771     if(this.handler){
34772         this.on("click", this.handler, this.scope, true);
34773     }
34774 };
34775
34776 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
34777     /**
34778      * @cfg {Function} handler
34779      * A function that will handle the click event of this menu item (defaults to undefined)
34780      */
34781     /**
34782      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
34783      */
34784     canActivate : false,
34785     /**
34786      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
34787      */
34788     activeClass : "x-menu-item-active",
34789     /**
34790      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
34791      */
34792     hideOnClick : true,
34793     /**
34794      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
34795      */
34796     hideDelay : 100,
34797
34798     // private
34799     ctype: "Roo.menu.BaseItem",
34800
34801     // private
34802     actionMode : "container",
34803
34804     // private
34805     render : function(container, parentMenu){
34806         this.parentMenu = parentMenu;
34807         Roo.menu.BaseItem.superclass.render.call(this, container);
34808         this.container.menuItemId = this.id;
34809     },
34810
34811     // private
34812     onRender : function(container, position){
34813         this.el = Roo.get(this.el);
34814         container.dom.appendChild(this.el.dom);
34815     },
34816
34817     // private
34818     onClick : function(e){
34819         if(!this.disabled && this.fireEvent("click", this, e) !== false
34820                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
34821             this.handleClick(e);
34822         }else{
34823             e.stopEvent();
34824         }
34825     },
34826
34827     // private
34828     activate : function(){
34829         if(this.disabled){
34830             return false;
34831         }
34832         var li = this.container;
34833         li.addClass(this.activeClass);
34834         this.region = li.getRegion().adjust(2, 2, -2, -2);
34835         this.fireEvent("activate", this);
34836         return true;
34837     },
34838
34839     // private
34840     deactivate : function(){
34841         this.container.removeClass(this.activeClass);
34842         this.fireEvent("deactivate", this);
34843     },
34844
34845     // private
34846     shouldDeactivate : function(e){
34847         return !this.region || !this.region.contains(e.getPoint());
34848     },
34849
34850     // private
34851     handleClick : function(e){
34852         if(this.hideOnClick){
34853             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
34854         }
34855     },
34856
34857     // private
34858     expandMenu : function(autoActivate){
34859         // do nothing
34860     },
34861
34862     // private
34863     hideMenu : function(){
34864         // do nothing
34865     }
34866 });/*
34867  * Based on:
34868  * Ext JS Library 1.1.1
34869  * Copyright(c) 2006-2007, Ext JS, LLC.
34870  *
34871  * Originally Released Under LGPL - original licence link has changed is not relivant.
34872  *
34873  * Fork - LGPL
34874  * <script type="text/javascript">
34875  */
34876  
34877 /**
34878  * @class Roo.menu.Adapter
34879  * @extends Roo.menu.BaseItem
34880  * 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.
34881  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
34882  * @constructor
34883  * Creates a new Adapter
34884  * @param {Object} config Configuration options
34885  */
34886 Roo.menu.Adapter = function(component, config){
34887     Roo.menu.Adapter.superclass.constructor.call(this, config);
34888     this.component = component;
34889 };
34890 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
34891     // private
34892     canActivate : true,
34893
34894     // private
34895     onRender : function(container, position){
34896         this.component.render(container);
34897         this.el = this.component.getEl();
34898     },
34899
34900     // private
34901     activate : function(){
34902         if(this.disabled){
34903             return false;
34904         }
34905         this.component.focus();
34906         this.fireEvent("activate", this);
34907         return true;
34908     },
34909
34910     // private
34911     deactivate : function(){
34912         this.fireEvent("deactivate", this);
34913     },
34914
34915     // private
34916     disable : function(){
34917         this.component.disable();
34918         Roo.menu.Adapter.superclass.disable.call(this);
34919     },
34920
34921     // private
34922     enable : function(){
34923         this.component.enable();
34924         Roo.menu.Adapter.superclass.enable.call(this);
34925     }
34926 });/*
34927  * Based on:
34928  * Ext JS Library 1.1.1
34929  * Copyright(c) 2006-2007, Ext JS, LLC.
34930  *
34931  * Originally Released Under LGPL - original licence link has changed is not relivant.
34932  *
34933  * Fork - LGPL
34934  * <script type="text/javascript">
34935  */
34936
34937 /**
34938  * @class Roo.menu.TextItem
34939  * @extends Roo.menu.BaseItem
34940  * Adds a static text string to a menu, usually used as either a heading or group separator.
34941  * Note: old style constructor with text is still supported.
34942  * 
34943  * @constructor
34944  * Creates a new TextItem
34945  * @param {Object} cfg Configuration
34946  */
34947 Roo.menu.TextItem = function(cfg){
34948     if (typeof(cfg) == 'string') {
34949         this.text = cfg;
34950     } else {
34951         Roo.apply(this,cfg);
34952     }
34953     
34954     Roo.menu.TextItem.superclass.constructor.call(this);
34955 };
34956
34957 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
34958     /**
34959      * @cfg {Boolean} text Text to show on item.
34960      */
34961     text : '',
34962     
34963     /**
34964      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
34965      */
34966     hideOnClick : false,
34967     /**
34968      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
34969      */
34970     itemCls : "x-menu-text",
34971
34972     // private
34973     onRender : function(){
34974         var s = document.createElement("span");
34975         s.className = this.itemCls;
34976         s.innerHTML = this.text;
34977         this.el = s;
34978         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
34979     }
34980 });/*
34981  * Based on:
34982  * Ext JS Library 1.1.1
34983  * Copyright(c) 2006-2007, Ext JS, LLC.
34984  *
34985  * Originally Released Under LGPL - original licence link has changed is not relivant.
34986  *
34987  * Fork - LGPL
34988  * <script type="text/javascript">
34989  */
34990
34991 /**
34992  * @class Roo.menu.Separator
34993  * @extends Roo.menu.BaseItem
34994  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
34995  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
34996  * @constructor
34997  * @param {Object} config Configuration options
34998  */
34999 Roo.menu.Separator = function(config){
35000     Roo.menu.Separator.superclass.constructor.call(this, config);
35001 };
35002
35003 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
35004     /**
35005      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
35006      */
35007     itemCls : "x-menu-sep",
35008     /**
35009      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
35010      */
35011     hideOnClick : false,
35012
35013     // private
35014     onRender : function(li){
35015         var s = document.createElement("span");
35016         s.className = this.itemCls;
35017         s.innerHTML = "&#160;";
35018         this.el = s;
35019         li.addClass("x-menu-sep-li");
35020         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
35021     }
35022 });/*
35023  * Based on:
35024  * Ext JS Library 1.1.1
35025  * Copyright(c) 2006-2007, Ext JS, LLC.
35026  *
35027  * Originally Released Under LGPL - original licence link has changed is not relivant.
35028  *
35029  * Fork - LGPL
35030  * <script type="text/javascript">
35031  */
35032 /**
35033  * @class Roo.menu.Item
35034  * @extends Roo.menu.BaseItem
35035  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
35036  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
35037  * activation and click handling.
35038  * @constructor
35039  * Creates a new Item
35040  * @param {Object} config Configuration options
35041  */
35042 Roo.menu.Item = function(config){
35043     Roo.menu.Item.superclass.constructor.call(this, config);
35044     if(this.menu){
35045         this.menu = Roo.menu.MenuMgr.get(this.menu);
35046     }
35047 };
35048 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
35049     
35050     /**
35051      * @cfg {String} text
35052      * The text to show on the menu item.
35053      */
35054     text: '',
35055      /**
35056      * @cfg {String} HTML to render in menu
35057      * The text to show on the menu item (HTML version).
35058      */
35059     html: '',
35060     /**
35061      * @cfg {String} icon
35062      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
35063      */
35064     icon: undefined,
35065     /**
35066      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
35067      */
35068     itemCls : "x-menu-item",
35069     /**
35070      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
35071      */
35072     canActivate : true,
35073     /**
35074      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
35075      */
35076     showDelay: 200,
35077     // doc'd in BaseItem
35078     hideDelay: 200,
35079
35080     // private
35081     ctype: "Roo.menu.Item",
35082     
35083     // private
35084     onRender : function(container, position){
35085         var el = document.createElement("a");
35086         el.hideFocus = true;
35087         el.unselectable = "on";
35088         el.href = this.href || "#";
35089         if(this.hrefTarget){
35090             el.target = this.hrefTarget;
35091         }
35092         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
35093         
35094         var html = this.html.length ? this.html  : String.format('{0}',this.text);
35095         
35096         el.innerHTML = String.format(
35097                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
35098                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
35099         this.el = el;
35100         Roo.menu.Item.superclass.onRender.call(this, container, position);
35101     },
35102
35103     /**
35104      * Sets the text to display in this menu item
35105      * @param {String} text The text to display
35106      * @param {Boolean} isHTML true to indicate text is pure html.
35107      */
35108     setText : function(text, isHTML){
35109         if (isHTML) {
35110             this.html = text;
35111         } else {
35112             this.text = text;
35113             this.html = '';
35114         }
35115         if(this.rendered){
35116             var html = this.html.length ? this.html  : String.format('{0}',this.text);
35117      
35118             this.el.update(String.format(
35119                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
35120                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
35121             this.parentMenu.autoWidth();
35122         }
35123     },
35124
35125     // private
35126     handleClick : function(e){
35127         if(!this.href){ // if no link defined, stop the event automatically
35128             e.stopEvent();
35129         }
35130         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
35131     },
35132
35133     // private
35134     activate : function(autoExpand){
35135         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
35136             this.focus();
35137             if(autoExpand){
35138                 this.expandMenu();
35139             }
35140         }
35141         return true;
35142     },
35143
35144     // private
35145     shouldDeactivate : function(e){
35146         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
35147             if(this.menu && this.menu.isVisible()){
35148                 return !this.menu.getEl().getRegion().contains(e.getPoint());
35149             }
35150             return true;
35151         }
35152         return false;
35153     },
35154
35155     // private
35156     deactivate : function(){
35157         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
35158         this.hideMenu();
35159     },
35160
35161     // private
35162     expandMenu : function(autoActivate){
35163         if(!this.disabled && this.menu){
35164             clearTimeout(this.hideTimer);
35165             delete this.hideTimer;
35166             if(!this.menu.isVisible() && !this.showTimer){
35167                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
35168             }else if (this.menu.isVisible() && autoActivate){
35169                 this.menu.tryActivate(0, 1);
35170             }
35171         }
35172     },
35173
35174     // private
35175     deferExpand : function(autoActivate){
35176         delete this.showTimer;
35177         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
35178         if(autoActivate){
35179             this.menu.tryActivate(0, 1);
35180         }
35181     },
35182
35183     // private
35184     hideMenu : function(){
35185         clearTimeout(this.showTimer);
35186         delete this.showTimer;
35187         if(!this.hideTimer && this.menu && this.menu.isVisible()){
35188             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
35189         }
35190     },
35191
35192     // private
35193     deferHide : function(){
35194         delete this.hideTimer;
35195         this.menu.hide();
35196     }
35197 });/*
35198  * Based on:
35199  * Ext JS Library 1.1.1
35200  * Copyright(c) 2006-2007, Ext JS, LLC.
35201  *
35202  * Originally Released Under LGPL - original licence link has changed is not relivant.
35203  *
35204  * Fork - LGPL
35205  * <script type="text/javascript">
35206  */
35207  
35208 /**
35209  * @class Roo.menu.CheckItem
35210  * @extends Roo.menu.Item
35211  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
35212  * @constructor
35213  * Creates a new CheckItem
35214  * @param {Object} config Configuration options
35215  */
35216 Roo.menu.CheckItem = function(config){
35217     Roo.menu.CheckItem.superclass.constructor.call(this, config);
35218     this.addEvents({
35219         /**
35220          * @event beforecheckchange
35221          * Fires before the checked value is set, providing an opportunity to cancel if needed
35222          * @param {Roo.menu.CheckItem} this
35223          * @param {Boolean} checked The new checked value that will be set
35224          */
35225         "beforecheckchange" : true,
35226         /**
35227          * @event checkchange
35228          * Fires after the checked value has been set
35229          * @param {Roo.menu.CheckItem} this
35230          * @param {Boolean} checked The checked value that was set
35231          */
35232         "checkchange" : true
35233     });
35234     if(this.checkHandler){
35235         this.on('checkchange', this.checkHandler, this.scope);
35236     }
35237 };
35238 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
35239     /**
35240      * @cfg {String} group
35241      * All check items with the same group name will automatically be grouped into a single-select
35242      * radio button group (defaults to '')
35243      */
35244     /**
35245      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
35246      */
35247     itemCls : "x-menu-item x-menu-check-item",
35248     /**
35249      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
35250      */
35251     groupClass : "x-menu-group-item",
35252
35253     /**
35254      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
35255      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
35256      * initialized with checked = true will be rendered as checked.
35257      */
35258     checked: false,
35259
35260     // private
35261     ctype: "Roo.menu.CheckItem",
35262
35263     // private
35264     onRender : function(c){
35265         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
35266         if(this.group){
35267             this.el.addClass(this.groupClass);
35268         }
35269         Roo.menu.MenuMgr.registerCheckable(this);
35270         if(this.checked){
35271             this.checked = false;
35272             this.setChecked(true, true);
35273         }
35274     },
35275
35276     // private
35277     destroy : function(){
35278         if(this.rendered){
35279             Roo.menu.MenuMgr.unregisterCheckable(this);
35280         }
35281         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
35282     },
35283
35284     /**
35285      * Set the checked state of this item
35286      * @param {Boolean} checked The new checked value
35287      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
35288      */
35289     setChecked : function(state, suppressEvent){
35290         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
35291             if(this.container){
35292                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
35293             }
35294             this.checked = state;
35295             if(suppressEvent !== true){
35296                 this.fireEvent("checkchange", this, state);
35297             }
35298         }
35299     },
35300
35301     // private
35302     handleClick : function(e){
35303        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
35304            this.setChecked(!this.checked);
35305        }
35306        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
35307     }
35308 });/*
35309  * Based on:
35310  * Ext JS Library 1.1.1
35311  * Copyright(c) 2006-2007, Ext JS, LLC.
35312  *
35313  * Originally Released Under LGPL - original licence link has changed is not relivant.
35314  *
35315  * Fork - LGPL
35316  * <script type="text/javascript">
35317  */
35318  
35319 /**
35320  * @class Roo.menu.DateItem
35321  * @extends Roo.menu.Adapter
35322  * A menu item that wraps the {@link Roo.DatPicker} component.
35323  * @constructor
35324  * Creates a new DateItem
35325  * @param {Object} config Configuration options
35326  */
35327 Roo.menu.DateItem = function(config){
35328     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
35329     /** The Roo.DatePicker object @type Roo.DatePicker */
35330     this.picker = this.component;
35331     this.addEvents({select: true});
35332     
35333     this.picker.on("render", function(picker){
35334         picker.getEl().swallowEvent("click");
35335         picker.container.addClass("x-menu-date-item");
35336     });
35337
35338     this.picker.on("select", this.onSelect, this);
35339 };
35340
35341 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
35342     // private
35343     onSelect : function(picker, date){
35344         this.fireEvent("select", this, date, picker);
35345         Roo.menu.DateItem.superclass.handleClick.call(this);
35346     }
35347 });/*
35348  * Based on:
35349  * Ext JS Library 1.1.1
35350  * Copyright(c) 2006-2007, Ext JS, LLC.
35351  *
35352  * Originally Released Under LGPL - original licence link has changed is not relivant.
35353  *
35354  * Fork - LGPL
35355  * <script type="text/javascript">
35356  */
35357  
35358 /**
35359  * @class Roo.menu.ColorItem
35360  * @extends Roo.menu.Adapter
35361  * A menu item that wraps the {@link Roo.ColorPalette} component.
35362  * @constructor
35363  * Creates a new ColorItem
35364  * @param {Object} config Configuration options
35365  */
35366 Roo.menu.ColorItem = function(config){
35367     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
35368     /** The Roo.ColorPalette object @type Roo.ColorPalette */
35369     this.palette = this.component;
35370     this.relayEvents(this.palette, ["select"]);
35371     if(this.selectHandler){
35372         this.on('select', this.selectHandler, this.scope);
35373     }
35374 };
35375 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
35376  * Based on:
35377  * Ext JS Library 1.1.1
35378  * Copyright(c) 2006-2007, Ext JS, LLC.
35379  *
35380  * Originally Released Under LGPL - original licence link has changed is not relivant.
35381  *
35382  * Fork - LGPL
35383  * <script type="text/javascript">
35384  */
35385  
35386
35387 /**
35388  * @class Roo.menu.DateMenu
35389  * @extends Roo.menu.Menu
35390  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
35391  * @constructor
35392  * Creates a new DateMenu
35393  * @param {Object} config Configuration options
35394  */
35395 Roo.menu.DateMenu = function(config){
35396     Roo.menu.DateMenu.superclass.constructor.call(this, config);
35397     this.plain = true;
35398     var di = new Roo.menu.DateItem(config);
35399     this.add(di);
35400     /**
35401      * The {@link Roo.DatePicker} instance for this DateMenu
35402      * @type DatePicker
35403      */
35404     this.picker = di.picker;
35405     /**
35406      * @event select
35407      * @param {DatePicker} picker
35408      * @param {Date} date
35409      */
35410     this.relayEvents(di, ["select"]);
35411
35412     this.on('beforeshow', function(){
35413         if(this.picker){
35414             this.picker.hideMonthPicker(true);
35415         }
35416     }, this);
35417 };
35418 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
35419     cls:'x-date-menu'
35420 });/*
35421  * Based on:
35422  * Ext JS Library 1.1.1
35423  * Copyright(c) 2006-2007, Ext JS, LLC.
35424  *
35425  * Originally Released Under LGPL - original licence link has changed is not relivant.
35426  *
35427  * Fork - LGPL
35428  * <script type="text/javascript">
35429  */
35430  
35431
35432 /**
35433  * @class Roo.menu.ColorMenu
35434  * @extends Roo.menu.Menu
35435  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
35436  * @constructor
35437  * Creates a new ColorMenu
35438  * @param {Object} config Configuration options
35439  */
35440 Roo.menu.ColorMenu = function(config){
35441     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
35442     this.plain = true;
35443     var ci = new Roo.menu.ColorItem(config);
35444     this.add(ci);
35445     /**
35446      * The {@link Roo.ColorPalette} instance for this ColorMenu
35447      * @type ColorPalette
35448      */
35449     this.palette = ci.palette;
35450     /**
35451      * @event select
35452      * @param {ColorPalette} palette
35453      * @param {String} color
35454      */
35455     this.relayEvents(ci, ["select"]);
35456 };
35457 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
35458  * Based on:
35459  * Ext JS Library 1.1.1
35460  * Copyright(c) 2006-2007, Ext JS, LLC.
35461  *
35462  * Originally Released Under LGPL - original licence link has changed is not relivant.
35463  *
35464  * Fork - LGPL
35465  * <script type="text/javascript">
35466  */
35467  
35468 /**
35469  * @class Roo.form.Field
35470  * @extends Roo.BoxComponent
35471  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
35472  * @constructor
35473  * Creates a new Field
35474  * @param {Object} config Configuration options
35475  */
35476 Roo.form.Field = function(config){
35477     Roo.form.Field.superclass.constructor.call(this, config);
35478 };
35479
35480 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
35481     /**
35482      * @cfg {String} fieldLabel Label to use when rendering a form.
35483      */
35484        /**
35485      * @cfg {String} qtip Mouse over tip
35486      */
35487      
35488     /**
35489      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
35490      */
35491     invalidClass : "x-form-invalid",
35492     /**
35493      * @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")
35494      */
35495     invalidText : "The value in this field is invalid",
35496     /**
35497      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
35498      */
35499     focusClass : "x-form-focus",
35500     /**
35501      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
35502       automatic validation (defaults to "keyup").
35503      */
35504     validationEvent : "keyup",
35505     /**
35506      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
35507      */
35508     validateOnBlur : true,
35509     /**
35510      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
35511      */
35512     validationDelay : 250,
35513     /**
35514      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
35515      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
35516      */
35517     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
35518     /**
35519      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
35520      */
35521     fieldClass : "x-form-field",
35522     /**
35523      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
35524      *<pre>
35525 Value         Description
35526 -----------   ----------------------------------------------------------------------
35527 qtip          Display a quick tip when the user hovers over the field
35528 title         Display a default browser title attribute popup
35529 under         Add a block div beneath the field containing the error text
35530 side          Add an error icon to the right of the field with a popup on hover
35531 [element id]  Add the error text directly to the innerHTML of the specified element
35532 </pre>
35533      */
35534     msgTarget : 'qtip',
35535     /**
35536      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
35537      */
35538     msgFx : 'normal',
35539
35540     /**
35541      * @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.
35542      */
35543     readOnly : false,
35544
35545     /**
35546      * @cfg {Boolean} disabled True to disable the field (defaults to false).
35547      */
35548     disabled : false,
35549
35550     /**
35551      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
35552      */
35553     inputType : undefined,
35554     
35555     /**
35556      * @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).
35557          */
35558         tabIndex : undefined,
35559         
35560     // private
35561     isFormField : true,
35562
35563     // private
35564     hasFocus : false,
35565     /**
35566      * @property {Roo.Element} fieldEl
35567      * Element Containing the rendered Field (with label etc.)
35568      */
35569     /**
35570      * @cfg {Mixed} value A value to initialize this field with.
35571      */
35572     value : undefined,
35573
35574     /**
35575      * @cfg {String} name The field's HTML name attribute.
35576      */
35577     /**
35578      * @cfg {String} cls A CSS class to apply to the field's underlying element.
35579      */
35580
35581         // private ??
35582         initComponent : function(){
35583         Roo.form.Field.superclass.initComponent.call(this);
35584         this.addEvents({
35585             /**
35586              * @event focus
35587              * Fires when this field receives input focus.
35588              * @param {Roo.form.Field} this
35589              */
35590             focus : true,
35591             /**
35592              * @event blur
35593              * Fires when this field loses input focus.
35594              * @param {Roo.form.Field} this
35595              */
35596             blur : true,
35597             /**
35598              * @event specialkey
35599              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
35600              * {@link Roo.EventObject#getKey} to determine which key was pressed.
35601              * @param {Roo.form.Field} this
35602              * @param {Roo.EventObject} e The event object
35603              */
35604             specialkey : true,
35605             /**
35606              * @event change
35607              * Fires just before the field blurs if the field value has changed.
35608              * @param {Roo.form.Field} this
35609              * @param {Mixed} newValue The new value
35610              * @param {Mixed} oldValue The original value
35611              */
35612             change : true,
35613             /**
35614              * @event invalid
35615              * Fires after the field has been marked as invalid.
35616              * @param {Roo.form.Field} this
35617              * @param {String} msg The validation message
35618              */
35619             invalid : true,
35620             /**
35621              * @event valid
35622              * Fires after the field has been validated with no errors.
35623              * @param {Roo.form.Field} this
35624              */
35625             valid : true,
35626              /**
35627              * @event keyup
35628              * Fires after the key up
35629              * @param {Roo.form.Field} this
35630              * @param {Roo.EventObject}  e The event Object
35631              */
35632             keyup : true
35633         });
35634     },
35635
35636     /**
35637      * Returns the name attribute of the field if available
35638      * @return {String} name The field name
35639      */
35640     getName: function(){
35641          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
35642     },
35643
35644     // private
35645     onRender : function(ct, position){
35646         Roo.form.Field.superclass.onRender.call(this, ct, position);
35647         if(!this.el){
35648             var cfg = this.getAutoCreate();
35649             if(!cfg.name){
35650                 cfg.name = this.name || this.id;
35651             }
35652             if(this.inputType){
35653                 cfg.type = this.inputType;
35654             }
35655             this.el = ct.createChild(cfg, position);
35656         }
35657         var type = this.el.dom.type;
35658         if(type){
35659             if(type == 'password'){
35660                 type = 'text';
35661             }
35662             this.el.addClass('x-form-'+type);
35663         }
35664         if(this.readOnly){
35665             this.el.dom.readOnly = true;
35666         }
35667         if(this.tabIndex !== undefined){
35668             this.el.dom.setAttribute('tabIndex', this.tabIndex);
35669         }
35670
35671         this.el.addClass([this.fieldClass, this.cls]);
35672         this.initValue();
35673     },
35674
35675     /**
35676      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
35677      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
35678      * @return {Roo.form.Field} this
35679      */
35680     applyTo : function(target){
35681         this.allowDomMove = false;
35682         this.el = Roo.get(target);
35683         this.render(this.el.dom.parentNode);
35684         return this;
35685     },
35686
35687     // private
35688     initValue : function(){
35689         if(this.value !== undefined){
35690             this.setValue(this.value);
35691         }else if(this.el.dom.value.length > 0){
35692             this.setValue(this.el.dom.value);
35693         }
35694     },
35695
35696     /**
35697      * Returns true if this field has been changed since it was originally loaded and is not disabled.
35698      */
35699     isDirty : function() {
35700         if(this.disabled) {
35701             return false;
35702         }
35703         return String(this.getValue()) !== String(this.originalValue);
35704     },
35705
35706     // private
35707     afterRender : function(){
35708         Roo.form.Field.superclass.afterRender.call(this);
35709         this.initEvents();
35710     },
35711
35712     // private
35713     fireKey : function(e){
35714         //Roo.log('field ' + e.getKey());
35715         if(e.isNavKeyPress()){
35716             this.fireEvent("specialkey", this, e);
35717         }
35718     },
35719
35720     /**
35721      * Resets the current field value to the originally loaded value and clears any validation messages
35722      */
35723     reset : function(){
35724         this.setValue(this.originalValue);
35725         this.clearInvalid();
35726     },
35727
35728     // private
35729     initEvents : function(){
35730         // safari killled keypress - so keydown is now used..
35731         this.el.on("keydown" , this.fireKey,  this);
35732         this.el.on("focus", this.onFocus,  this);
35733         this.el.on("blur", this.onBlur,  this);
35734         this.el.relayEvent('keyup', this);
35735
35736         // reference to original value for reset
35737         this.originalValue = this.getValue();
35738     },
35739
35740     // private
35741     onFocus : function(){
35742         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
35743             this.el.addClass(this.focusClass);
35744         }
35745         if(!this.hasFocus){
35746             this.hasFocus = true;
35747             this.startValue = this.getValue();
35748             this.fireEvent("focus", this);
35749         }
35750     },
35751
35752     beforeBlur : Roo.emptyFn,
35753
35754     // private
35755     onBlur : function(){
35756         this.beforeBlur();
35757         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
35758             this.el.removeClass(this.focusClass);
35759         }
35760         this.hasFocus = false;
35761         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
35762             this.validate();
35763         }
35764         var v = this.getValue();
35765         if(String(v) !== String(this.startValue)){
35766             this.fireEvent('change', this, v, this.startValue);
35767         }
35768         this.fireEvent("blur", this);
35769     },
35770
35771     /**
35772      * Returns whether or not the field value is currently valid
35773      * @param {Boolean} preventMark True to disable marking the field invalid
35774      * @return {Boolean} True if the value is valid, else false
35775      */
35776     isValid : function(preventMark){
35777         if(this.disabled){
35778             return true;
35779         }
35780         var restore = this.preventMark;
35781         this.preventMark = preventMark === true;
35782         var v = this.validateValue(this.processValue(this.getRawValue()));
35783         this.preventMark = restore;
35784         return v;
35785     },
35786
35787     /**
35788      * Validates the field value
35789      * @return {Boolean} True if the value is valid, else false
35790      */
35791     validate : function(){
35792         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
35793             this.clearInvalid();
35794             return true;
35795         }
35796         return false;
35797     },
35798
35799     processValue : function(value){
35800         return value;
35801     },
35802
35803     // private
35804     // Subclasses should provide the validation implementation by overriding this
35805     validateValue : function(value){
35806         return true;
35807     },
35808
35809     /**
35810      * Mark this field as invalid
35811      * @param {String} msg The validation message
35812      */
35813     markInvalid : function(msg){
35814         if(!this.rendered || this.preventMark){ // not rendered
35815             return;
35816         }
35817         this.el.addClass(this.invalidClass);
35818         msg = msg || this.invalidText;
35819         switch(this.msgTarget){
35820             case 'qtip':
35821                 this.el.dom.qtip = msg;
35822                 this.el.dom.qclass = 'x-form-invalid-tip';
35823                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
35824                     Roo.QuickTips.enable();
35825                 }
35826                 break;
35827             case 'title':
35828                 this.el.dom.title = msg;
35829                 break;
35830             case 'under':
35831                 if(!this.errorEl){
35832                     var elp = this.el.findParent('.x-form-element', 5, true);
35833                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
35834                     this.errorEl.setWidth(elp.getWidth(true)-20);
35835                 }
35836                 this.errorEl.update(msg);
35837                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
35838                 break;
35839             case 'side':
35840                 if(!this.errorIcon){
35841                     var elp = this.el.findParent('.x-form-element', 5, true);
35842                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
35843                 }
35844                 this.alignErrorIcon();
35845                 this.errorIcon.dom.qtip = msg;
35846                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
35847                 this.errorIcon.show();
35848                 this.on('resize', this.alignErrorIcon, this);
35849                 break;
35850             default:
35851                 var t = Roo.getDom(this.msgTarget);
35852                 t.innerHTML = msg;
35853                 t.style.display = this.msgDisplay;
35854                 break;
35855         }
35856         this.fireEvent('invalid', this, msg);
35857     },
35858
35859     // private
35860     alignErrorIcon : function(){
35861         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
35862     },
35863
35864     /**
35865      * Clear any invalid styles/messages for this field
35866      */
35867     clearInvalid : function(){
35868         if(!this.rendered || this.preventMark){ // not rendered
35869             return;
35870         }
35871         this.el.removeClass(this.invalidClass);
35872         switch(this.msgTarget){
35873             case 'qtip':
35874                 this.el.dom.qtip = '';
35875                 break;
35876             case 'title':
35877                 this.el.dom.title = '';
35878                 break;
35879             case 'under':
35880                 if(this.errorEl){
35881                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
35882                 }
35883                 break;
35884             case 'side':
35885                 if(this.errorIcon){
35886                     this.errorIcon.dom.qtip = '';
35887                     this.errorIcon.hide();
35888                     this.un('resize', this.alignErrorIcon, this);
35889                 }
35890                 break;
35891             default:
35892                 var t = Roo.getDom(this.msgTarget);
35893                 t.innerHTML = '';
35894                 t.style.display = 'none';
35895                 break;
35896         }
35897         this.fireEvent('valid', this);
35898     },
35899
35900     /**
35901      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
35902      * @return {Mixed} value The field value
35903      */
35904     getRawValue : function(){
35905         var v = this.el.getValue();
35906         if(v === this.emptyText){
35907             v = '';
35908         }
35909         return v;
35910     },
35911
35912     /**
35913      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
35914      * @return {Mixed} value The field value
35915      */
35916     getValue : function(){
35917         var v = this.el.getValue();
35918         if(v === this.emptyText || v === undefined){
35919             v = '';
35920         }
35921         return v;
35922     },
35923
35924     /**
35925      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
35926      * @param {Mixed} value The value to set
35927      */
35928     setRawValue : function(v){
35929         return this.el.dom.value = (v === null || v === undefined ? '' : v);
35930     },
35931
35932     /**
35933      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
35934      * @param {Mixed} value The value to set
35935      */
35936     setValue : function(v){
35937         this.value = v;
35938         if(this.rendered){
35939             this.el.dom.value = (v === null || v === undefined ? '' : v);
35940             this.validate();
35941         }
35942     },
35943
35944     adjustSize : function(w, h){
35945         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
35946         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
35947         return s;
35948     },
35949
35950     adjustWidth : function(tag, w){
35951         tag = tag.toLowerCase();
35952         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
35953             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
35954                 if(tag == 'input'){
35955                     return w + 2;
35956                 }
35957                 if(tag = 'textarea'){
35958                     return w-2;
35959                 }
35960             }else if(Roo.isOpera){
35961                 if(tag == 'input'){
35962                     return w + 2;
35963                 }
35964                 if(tag = 'textarea'){
35965                     return w-2;
35966                 }
35967             }
35968         }
35969         return w;
35970     }
35971 });
35972
35973
35974 // anything other than normal should be considered experimental
35975 Roo.form.Field.msgFx = {
35976     normal : {
35977         show: function(msgEl, f){
35978             msgEl.setDisplayed('block');
35979         },
35980
35981         hide : function(msgEl, f){
35982             msgEl.setDisplayed(false).update('');
35983         }
35984     },
35985
35986     slide : {
35987         show: function(msgEl, f){
35988             msgEl.slideIn('t', {stopFx:true});
35989         },
35990
35991         hide : function(msgEl, f){
35992             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
35993         }
35994     },
35995
35996     slideRight : {
35997         show: function(msgEl, f){
35998             msgEl.fixDisplay();
35999             msgEl.alignTo(f.el, 'tl-tr');
36000             msgEl.slideIn('l', {stopFx:true});
36001         },
36002
36003         hide : function(msgEl, f){
36004             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
36005         }
36006     }
36007 };/*
36008  * Based on:
36009  * Ext JS Library 1.1.1
36010  * Copyright(c) 2006-2007, Ext JS, LLC.
36011  *
36012  * Originally Released Under LGPL - original licence link has changed is not relivant.
36013  *
36014  * Fork - LGPL
36015  * <script type="text/javascript">
36016  */
36017  
36018
36019 /**
36020  * @class Roo.form.TextField
36021  * @extends Roo.form.Field
36022  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
36023  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
36024  * @constructor
36025  * Creates a new TextField
36026  * @param {Object} config Configuration options
36027  */
36028 Roo.form.TextField = function(config){
36029     Roo.form.TextField.superclass.constructor.call(this, config);
36030     this.addEvents({
36031         /**
36032          * @event autosize
36033          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
36034          * according to the default logic, but this event provides a hook for the developer to apply additional
36035          * logic at runtime to resize the field if needed.
36036              * @param {Roo.form.Field} this This text field
36037              * @param {Number} width The new field width
36038              */
36039         autosize : true
36040     });
36041 };
36042
36043 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
36044     /**
36045      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
36046      */
36047     grow : false,
36048     /**
36049      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
36050      */
36051     growMin : 30,
36052     /**
36053      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
36054      */
36055     growMax : 800,
36056     /**
36057      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
36058      */
36059     vtype : null,
36060     /**
36061      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
36062      */
36063     maskRe : null,
36064     /**
36065      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
36066      */
36067     disableKeyFilter : false,
36068     /**
36069      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
36070      */
36071     allowBlank : true,
36072     /**
36073      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
36074      */
36075     minLength : 0,
36076     /**
36077      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
36078      */
36079     maxLength : Number.MAX_VALUE,
36080     /**
36081      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
36082      */
36083     minLengthText : "The minimum length for this field is {0}",
36084     /**
36085      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
36086      */
36087     maxLengthText : "The maximum length for this field is {0}",
36088     /**
36089      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
36090      */
36091     selectOnFocus : false,
36092     /**
36093      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
36094      */
36095     blankText : "This field is required",
36096     /**
36097      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
36098      * If available, this function will be called only after the basic validators all return true, and will be passed the
36099      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
36100      */
36101     validator : null,
36102     /**
36103      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
36104      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
36105      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
36106      */
36107     regex : null,
36108     /**
36109      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
36110      */
36111     regexText : "",
36112     /**
36113      * @cfg {String} emptyText The default text to display in an empty field (defaults to null).
36114      */
36115     emptyText : null,
36116     /**
36117      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the {@link #emptyText} (defaults to
36118      * 'x-form-empty-field').  This class is automatically added and removed as needed depending on the current field value.
36119      */
36120     emptyClass : 'x-form-empty-field',
36121
36122     // private
36123     initEvents : function(){
36124         Roo.form.TextField.superclass.initEvents.call(this);
36125         if(this.validationEvent == 'keyup'){
36126             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
36127             this.el.on('keyup', this.filterValidation, this);
36128         }
36129         else if(this.validationEvent !== false){
36130             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
36131         }
36132         if(this.selectOnFocus || this.emptyText){
36133             this.on("focus", this.preFocus, this);
36134             if(this.emptyText){
36135                 this.on('blur', this.postBlur, this);
36136                 this.applyEmptyText();
36137             }
36138         }
36139         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
36140             this.el.on("keypress", this.filterKeys, this);
36141         }
36142         if(this.grow){
36143             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
36144             this.el.on("click", this.autoSize,  this);
36145         }
36146     },
36147
36148     processValue : function(value){
36149         if(this.stripCharsRe){
36150             var newValue = value.replace(this.stripCharsRe, '');
36151             if(newValue !== value){
36152                 this.setRawValue(newValue);
36153                 return newValue;
36154             }
36155         }
36156         return value;
36157     },
36158
36159     filterValidation : function(e){
36160         if(!e.isNavKeyPress()){
36161             this.validationTask.delay(this.validationDelay);
36162         }
36163     },
36164
36165     // private
36166     onKeyUp : function(e){
36167         if(!e.isNavKeyPress()){
36168             this.autoSize();
36169         }
36170     },
36171
36172     /**
36173      * Resets the current field value to the originally-loaded value and clears any validation messages.
36174      * Also adds emptyText and emptyClass if the original value was blank.
36175      */
36176     reset : function(){
36177         Roo.form.TextField.superclass.reset.call(this);
36178         this.applyEmptyText();
36179     },
36180
36181     applyEmptyText : function(){
36182         if(this.rendered && this.emptyText && this.getRawValue().length < 1){
36183             this.setRawValue(this.emptyText);
36184             this.el.addClass(this.emptyClass);
36185         }
36186     },
36187
36188     // private
36189     preFocus : function(){
36190         if(this.emptyText){
36191             if(this.el.dom.value == this.emptyText){
36192                 this.setRawValue('');
36193             }
36194             this.el.removeClass(this.emptyClass);
36195         }
36196         if(this.selectOnFocus){
36197             this.el.dom.select();
36198         }
36199     },
36200
36201     // private
36202     postBlur : function(){
36203         this.applyEmptyText();
36204     },
36205
36206     // private
36207     filterKeys : function(e){
36208         var k = e.getKey();
36209         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
36210             return;
36211         }
36212         var c = e.getCharCode(), cc = String.fromCharCode(c);
36213         if(Roo.isIE && (e.isSpecialKey() || !cc)){
36214             return;
36215         }
36216         if(!this.maskRe.test(cc)){
36217             e.stopEvent();
36218         }
36219     },
36220
36221     setValue : function(v){
36222         if(this.emptyText && this.el && v !== undefined && v !== null && v !== ''){
36223             this.el.removeClass(this.emptyClass);
36224         }
36225         Roo.form.TextField.superclass.setValue.apply(this, arguments);
36226         this.applyEmptyText();
36227         this.autoSize();
36228     },
36229
36230     /**
36231      * Validates a value according to the field's validation rules and marks the field as invalid
36232      * if the validation fails
36233      * @param {Mixed} value The value to validate
36234      * @return {Boolean} True if the value is valid, else false
36235      */
36236     validateValue : function(value){
36237         if(value.length < 1 || value === this.emptyText){ // if it's blank
36238              if(this.allowBlank){
36239                 this.clearInvalid();
36240                 return true;
36241              }else{
36242                 this.markInvalid(this.blankText);
36243                 return false;
36244              }
36245         }
36246         if(value.length < this.minLength){
36247             this.markInvalid(String.format(this.minLengthText, this.minLength));
36248             return false;
36249         }
36250         if(value.length > this.maxLength){
36251             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
36252             return false;
36253         }
36254         if(this.vtype){
36255             var vt = Roo.form.VTypes;
36256             if(!vt[this.vtype](value, this)){
36257                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
36258                 return false;
36259             }
36260         }
36261         if(typeof this.validator == "function"){
36262             var msg = this.validator(value);
36263             if(msg !== true){
36264                 this.markInvalid(msg);
36265                 return false;
36266             }
36267         }
36268         if(this.regex && !this.regex.test(value)){
36269             this.markInvalid(this.regexText);
36270             return false;
36271         }
36272         return true;
36273     },
36274
36275     /**
36276      * Selects text in this field
36277      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
36278      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
36279      */
36280     selectText : function(start, end){
36281         var v = this.getRawValue();
36282         if(v.length > 0){
36283             start = start === undefined ? 0 : start;
36284             end = end === undefined ? v.length : end;
36285             var d = this.el.dom;
36286             if(d.setSelectionRange){
36287                 d.setSelectionRange(start, end);
36288             }else if(d.createTextRange){
36289                 var range = d.createTextRange();
36290                 range.moveStart("character", start);
36291                 range.moveEnd("character", v.length-end);
36292                 range.select();
36293             }
36294         }
36295     },
36296
36297     /**
36298      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
36299      * This only takes effect if grow = true, and fires the autosize event.
36300      */
36301     autoSize : function(){
36302         if(!this.grow || !this.rendered){
36303             return;
36304         }
36305         if(!this.metrics){
36306             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
36307         }
36308         var el = this.el;
36309         var v = el.dom.value;
36310         var d = document.createElement('div');
36311         d.appendChild(document.createTextNode(v));
36312         v = d.innerHTML;
36313         d = null;
36314         v += "&#160;";
36315         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
36316         this.el.setWidth(w);
36317         this.fireEvent("autosize", this, w);
36318     }
36319 });/*
36320  * Based on:
36321  * Ext JS Library 1.1.1
36322  * Copyright(c) 2006-2007, Ext JS, LLC.
36323  *
36324  * Originally Released Under LGPL - original licence link has changed is not relivant.
36325  *
36326  * Fork - LGPL
36327  * <script type="text/javascript">
36328  */
36329  
36330 /**
36331  * @class Roo.form.Hidden
36332  * @extends Roo.form.TextField
36333  * Simple Hidden element used on forms 
36334  * 
36335  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
36336  * 
36337  * @constructor
36338  * Creates a new Hidden form element.
36339  * @param {Object} config Configuration options
36340  */
36341
36342
36343
36344 // easy hidden field...
36345 Roo.form.Hidden = function(config){
36346     Roo.form.Hidden.superclass.constructor.call(this, config);
36347 };
36348   
36349 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
36350     fieldLabel:      '',
36351     inputType:      'hidden',
36352     width:          50,
36353     allowBlank:     true,
36354     labelSeparator: '',
36355     hidden:         true,
36356     itemCls :       'x-form-item-display-none'
36357
36358
36359 });
36360
36361
36362 /*
36363  * Based on:
36364  * Ext JS Library 1.1.1
36365  * Copyright(c) 2006-2007, Ext JS, LLC.
36366  *
36367  * Originally Released Under LGPL - original licence link has changed is not relivant.
36368  *
36369  * Fork - LGPL
36370  * <script type="text/javascript">
36371  */
36372  
36373 /**
36374  * @class Roo.form.TriggerField
36375  * @extends Roo.form.TextField
36376  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
36377  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
36378  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
36379  * for which you can provide a custom implementation.  For example:
36380  * <pre><code>
36381 var trigger = new Roo.form.TriggerField();
36382 trigger.onTriggerClick = myTriggerFn;
36383 trigger.applyTo('my-field');
36384 </code></pre>
36385  *
36386  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
36387  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
36388  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
36389  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
36390  * @constructor
36391  * Create a new TriggerField.
36392  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
36393  * to the base TextField)
36394  */
36395 Roo.form.TriggerField = function(config){
36396     this.mimicing = false;
36397     Roo.form.TriggerField.superclass.constructor.call(this, config);
36398 };
36399
36400 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
36401     /**
36402      * @cfg {String} triggerClass A CSS class to apply to the trigger
36403      */
36404     /**
36405      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36406      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
36407      */
36408     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
36409     /**
36410      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
36411      */
36412     hideTrigger:false,
36413
36414     /** @cfg {Boolean} grow @hide */
36415     /** @cfg {Number} growMin @hide */
36416     /** @cfg {Number} growMax @hide */
36417
36418     /**
36419      * @hide 
36420      * @method
36421      */
36422     autoSize: Roo.emptyFn,
36423     // private
36424     monitorTab : true,
36425     // private
36426     deferHeight : true,
36427
36428     
36429     actionMode : 'wrap',
36430     // private
36431     onResize : function(w, h){
36432         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
36433         if(typeof w == 'number'){
36434             var x = w - this.trigger.getWidth();
36435             this.el.setWidth(this.adjustWidth('input', x));
36436             this.trigger.setStyle('left', x+'px');
36437         }
36438     },
36439
36440     // private
36441     adjustSize : Roo.BoxComponent.prototype.adjustSize,
36442
36443     // private
36444     getResizeEl : function(){
36445         return this.wrap;
36446     },
36447
36448     // private
36449     getPositionEl : function(){
36450         return this.wrap;
36451     },
36452
36453     // private
36454     alignErrorIcon : function(){
36455         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
36456     },
36457
36458     // private
36459     onRender : function(ct, position){
36460         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
36461         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
36462         this.trigger = this.wrap.createChild(this.triggerConfig ||
36463                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
36464         if(this.hideTrigger){
36465             this.trigger.setDisplayed(false);
36466         }
36467         this.initTrigger();
36468         if(!this.width){
36469             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
36470         }
36471     },
36472
36473     // private
36474     initTrigger : function(){
36475         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
36476         this.trigger.addClassOnOver('x-form-trigger-over');
36477         this.trigger.addClassOnClick('x-form-trigger-click');
36478     },
36479
36480     // private
36481     onDestroy : function(){
36482         if(this.trigger){
36483             this.trigger.removeAllListeners();
36484             this.trigger.remove();
36485         }
36486         if(this.wrap){
36487             this.wrap.remove();
36488         }
36489         Roo.form.TriggerField.superclass.onDestroy.call(this);
36490     },
36491
36492     // private
36493     onFocus : function(){
36494         Roo.form.TriggerField.superclass.onFocus.call(this);
36495         if(!this.mimicing){
36496             this.wrap.addClass('x-trigger-wrap-focus');
36497             this.mimicing = true;
36498             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
36499             if(this.monitorTab){
36500                 this.el.on("keydown", this.checkTab, this);
36501             }
36502         }
36503     },
36504
36505     // private
36506     checkTab : function(e){
36507         if(e.getKey() == e.TAB){
36508             this.triggerBlur();
36509         }
36510     },
36511
36512     // private
36513     onBlur : function(){
36514         // do nothing
36515     },
36516
36517     // private
36518     mimicBlur : function(e, t){
36519         if(!this.wrap.contains(t) && this.validateBlur()){
36520             this.triggerBlur();
36521         }
36522     },
36523
36524     // private
36525     triggerBlur : function(){
36526         this.mimicing = false;
36527         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
36528         if(this.monitorTab){
36529             this.el.un("keydown", this.checkTab, this);
36530         }
36531         this.wrap.removeClass('x-trigger-wrap-focus');
36532         Roo.form.TriggerField.superclass.onBlur.call(this);
36533     },
36534
36535     // private
36536     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
36537     validateBlur : function(e, t){
36538         return true;
36539     },
36540
36541     // private
36542     onDisable : function(){
36543         Roo.form.TriggerField.superclass.onDisable.call(this);
36544         if(this.wrap){
36545             this.wrap.addClass('x-item-disabled');
36546         }
36547     },
36548
36549     // private
36550     onEnable : function(){
36551         Roo.form.TriggerField.superclass.onEnable.call(this);
36552         if(this.wrap){
36553             this.wrap.removeClass('x-item-disabled');
36554         }
36555     },
36556
36557     // private
36558     onShow : function(){
36559         var ae = this.getActionEl();
36560         
36561         if(ae){
36562             ae.dom.style.display = '';
36563             ae.dom.style.visibility = 'visible';
36564         }
36565     },
36566
36567     // private
36568     
36569     onHide : function(){
36570         var ae = this.getActionEl();
36571         ae.dom.style.display = 'none';
36572     },
36573
36574     /**
36575      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
36576      * by an implementing function.
36577      * @method
36578      * @param {EventObject} e
36579      */
36580     onTriggerClick : Roo.emptyFn
36581 });
36582
36583 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
36584 // to be extended by an implementing class.  For an example of implementing this class, see the custom
36585 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
36586 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
36587     initComponent : function(){
36588         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
36589
36590         this.triggerConfig = {
36591             tag:'span', cls:'x-form-twin-triggers', cn:[
36592             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
36593             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
36594         ]};
36595     },
36596
36597     getTrigger : function(index){
36598         return this.triggers[index];
36599     },
36600
36601     initTrigger : function(){
36602         var ts = this.trigger.select('.x-form-trigger', true);
36603         this.wrap.setStyle('overflow', 'hidden');
36604         var triggerField = this;
36605         ts.each(function(t, all, index){
36606             t.hide = function(){
36607                 var w = triggerField.wrap.getWidth();
36608                 this.dom.style.display = 'none';
36609                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
36610             };
36611             t.show = function(){
36612                 var w = triggerField.wrap.getWidth();
36613                 this.dom.style.display = '';
36614                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
36615             };
36616             var triggerIndex = 'Trigger'+(index+1);
36617
36618             if(this['hide'+triggerIndex]){
36619                 t.dom.style.display = 'none';
36620             }
36621             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
36622             t.addClassOnOver('x-form-trigger-over');
36623             t.addClassOnClick('x-form-trigger-click');
36624         }, this);
36625         this.triggers = ts.elements;
36626     },
36627
36628     onTrigger1Click : Roo.emptyFn,
36629     onTrigger2Click : Roo.emptyFn
36630 });/*
36631  * Based on:
36632  * Ext JS Library 1.1.1
36633  * Copyright(c) 2006-2007, Ext JS, LLC.
36634  *
36635  * Originally Released Under LGPL - original licence link has changed is not relivant.
36636  *
36637  * Fork - LGPL
36638  * <script type="text/javascript">
36639  */
36640  
36641 /**
36642  * @class Roo.form.TextArea
36643  * @extends Roo.form.TextField
36644  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
36645  * support for auto-sizing.
36646  * @constructor
36647  * Creates a new TextArea
36648  * @param {Object} config Configuration options
36649  */
36650 Roo.form.TextArea = function(config){
36651     Roo.form.TextArea.superclass.constructor.call(this, config);
36652     // these are provided exchanges for backwards compat
36653     // minHeight/maxHeight were replaced by growMin/growMax to be
36654     // compatible with TextField growing config values
36655     if(this.minHeight !== undefined){
36656         this.growMin = this.minHeight;
36657     }
36658     if(this.maxHeight !== undefined){
36659         this.growMax = this.maxHeight;
36660     }
36661 };
36662
36663 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
36664     /**
36665      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
36666      */
36667     growMin : 60,
36668     /**
36669      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
36670      */
36671     growMax: 1000,
36672     /**
36673      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
36674      * in the field (equivalent to setting overflow: hidden, defaults to false)
36675      */
36676     preventScrollbars: false,
36677     /**
36678      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36679      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
36680      */
36681
36682     // private
36683     onRender : function(ct, position){
36684         if(!this.el){
36685             this.defaultAutoCreate = {
36686                 tag: "textarea",
36687                 style:"width:300px;height:60px;",
36688                 autocomplete: "off"
36689             };
36690         }
36691         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
36692         if(this.grow){
36693             this.textSizeEl = Roo.DomHelper.append(document.body, {
36694                 tag: "pre", cls: "x-form-grow-sizer"
36695             });
36696             if(this.preventScrollbars){
36697                 this.el.setStyle("overflow", "hidden");
36698             }
36699             this.el.setHeight(this.growMin);
36700         }
36701     },
36702
36703     onDestroy : function(){
36704         if(this.textSizeEl){
36705             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
36706         }
36707         Roo.form.TextArea.superclass.onDestroy.call(this);
36708     },
36709
36710     // private
36711     onKeyUp : function(e){
36712         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
36713             this.autoSize();
36714         }
36715     },
36716
36717     /**
36718      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
36719      * This only takes effect if grow = true, and fires the autosize event if the height changes.
36720      */
36721     autoSize : function(){
36722         if(!this.grow || !this.textSizeEl){
36723             return;
36724         }
36725         var el = this.el;
36726         var v = el.dom.value;
36727         var ts = this.textSizeEl;
36728
36729         ts.innerHTML = '';
36730         ts.appendChild(document.createTextNode(v));
36731         v = ts.innerHTML;
36732
36733         Roo.fly(ts).setWidth(this.el.getWidth());
36734         if(v.length < 1){
36735             v = "&#160;&#160;";
36736         }else{
36737             if(Roo.isIE){
36738                 v = v.replace(/\n/g, '<p>&#160;</p>');
36739             }
36740             v += "&#160;\n&#160;";
36741         }
36742         ts.innerHTML = v;
36743         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
36744         if(h != this.lastHeight){
36745             this.lastHeight = h;
36746             this.el.setHeight(h);
36747             this.fireEvent("autosize", this, h);
36748         }
36749     }
36750 });/*
36751  * Based on:
36752  * Ext JS Library 1.1.1
36753  * Copyright(c) 2006-2007, Ext JS, LLC.
36754  *
36755  * Originally Released Under LGPL - original licence link has changed is not relivant.
36756  *
36757  * Fork - LGPL
36758  * <script type="text/javascript">
36759  */
36760  
36761
36762 /**
36763  * @class Roo.form.NumberField
36764  * @extends Roo.form.TextField
36765  * Numeric text field that provides automatic keystroke filtering and numeric validation.
36766  * @constructor
36767  * Creates a new NumberField
36768  * @param {Object} config Configuration options
36769  */
36770 Roo.form.NumberField = function(config){
36771     Roo.form.NumberField.superclass.constructor.call(this, config);
36772 };
36773
36774 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
36775     /**
36776      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
36777      */
36778     fieldClass: "x-form-field x-form-num-field",
36779     /**
36780      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36781      */
36782     allowDecimals : true,
36783     /**
36784      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36785      */
36786     decimalSeparator : ".",
36787     /**
36788      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36789      */
36790     decimalPrecision : 2,
36791     /**
36792      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36793      */
36794     allowNegative : true,
36795     /**
36796      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36797      */
36798     minValue : Number.NEGATIVE_INFINITY,
36799     /**
36800      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36801      */
36802     maxValue : Number.MAX_VALUE,
36803     /**
36804      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36805      */
36806     minText : "The minimum value for this field is {0}",
36807     /**
36808      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36809      */
36810     maxText : "The maximum value for this field is {0}",
36811     /**
36812      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36813      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36814      */
36815     nanText : "{0} is not a valid number",
36816
36817     // private
36818     initEvents : function(){
36819         Roo.form.NumberField.superclass.initEvents.call(this);
36820         var allowed = "0123456789";
36821         if(this.allowDecimals){
36822             allowed += this.decimalSeparator;
36823         }
36824         if(this.allowNegative){
36825             allowed += "-";
36826         }
36827         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36828         var keyPress = function(e){
36829             var k = e.getKey();
36830             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36831                 return;
36832             }
36833             var c = e.getCharCode();
36834             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36835                 e.stopEvent();
36836             }
36837         };
36838         this.el.on("keypress", keyPress, this);
36839     },
36840
36841     // private
36842     validateValue : function(value){
36843         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
36844             return false;
36845         }
36846         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
36847              return true;
36848         }
36849         var num = this.parseValue(value);
36850         if(isNaN(num)){
36851             this.markInvalid(String.format(this.nanText, value));
36852             return false;
36853         }
36854         if(num < this.minValue){
36855             this.markInvalid(String.format(this.minText, this.minValue));
36856             return false;
36857         }
36858         if(num > this.maxValue){
36859             this.markInvalid(String.format(this.maxText, this.maxValue));
36860             return false;
36861         }
36862         return true;
36863     },
36864
36865     getValue : function(){
36866         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
36867     },
36868
36869     // private
36870     parseValue : function(value){
36871         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36872         return isNaN(value) ? '' : value;
36873     },
36874
36875     // private
36876     fixPrecision : function(value){
36877         var nan = isNaN(value);
36878         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36879             return nan ? '' : value;
36880         }
36881         return parseFloat(value).toFixed(this.decimalPrecision);
36882     },
36883
36884     setValue : function(v){
36885         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
36886     },
36887
36888     // private
36889     decimalPrecisionFcn : function(v){
36890         return Math.floor(v);
36891     },
36892
36893     beforeBlur : function(){
36894         var v = this.parseValue(this.getRawValue());
36895         if(v){
36896             this.setValue(this.fixPrecision(v));
36897         }
36898     }
36899 });/*
36900  * Based on:
36901  * Ext JS Library 1.1.1
36902  * Copyright(c) 2006-2007, Ext JS, LLC.
36903  *
36904  * Originally Released Under LGPL - original licence link has changed is not relivant.
36905  *
36906  * Fork - LGPL
36907  * <script type="text/javascript">
36908  */
36909  
36910 /**
36911  * @class Roo.form.DateField
36912  * @extends Roo.form.TriggerField
36913  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
36914 * @constructor
36915 * Create a new DateField
36916 * @param {Object} config
36917  */
36918 Roo.form.DateField = function(config){
36919     Roo.form.DateField.superclass.constructor.call(this, config);
36920     
36921       this.addEvents({
36922          
36923         /**
36924          * @event select
36925          * Fires when a date is selected
36926              * @param {Roo.form.DateField} combo This combo box
36927              * @param {Date} date The date selected
36928              */
36929         'select' : true
36930          
36931     });
36932     
36933     
36934     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
36935     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
36936     this.ddMatch = null;
36937     if(this.disabledDates){
36938         var dd = this.disabledDates;
36939         var re = "(?:";
36940         for(var i = 0; i < dd.length; i++){
36941             re += dd[i];
36942             if(i != dd.length-1) re += "|";
36943         }
36944         this.ddMatch = new RegExp(re + ")");
36945     }
36946 };
36947
36948 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
36949     /**
36950      * @cfg {String} format
36951      * The default date format string which can be overriden for localization support.  The format must be
36952      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
36953      */
36954     format : "m/d/y",
36955     /**
36956      * @cfg {String} altFormats
36957      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
36958      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
36959      */
36960     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
36961     /**
36962      * @cfg {Array} disabledDays
36963      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
36964      */
36965     disabledDays : null,
36966     /**
36967      * @cfg {String} disabledDaysText
36968      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
36969      */
36970     disabledDaysText : "Disabled",
36971     /**
36972      * @cfg {Array} disabledDates
36973      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
36974      * expression so they are very powerful. Some examples:
36975      * <ul>
36976      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
36977      * <li>["03/08", "09/16"] would disable those days for every year</li>
36978      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
36979      * <li>["03/../2006"] would disable every day in March 2006</li>
36980      * <li>["^03"] would disable every day in every March</li>
36981      * </ul>
36982      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
36983      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
36984      */
36985     disabledDates : null,
36986     /**
36987      * @cfg {String} disabledDatesText
36988      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
36989      */
36990     disabledDatesText : "Disabled",
36991     /**
36992      * @cfg {Date/String} minValue
36993      * The minimum allowed date. Can be either a Javascript date object or a string date in a
36994      * valid format (defaults to null).
36995      */
36996     minValue : null,
36997     /**
36998      * @cfg {Date/String} maxValue
36999      * The maximum allowed date. Can be either a Javascript date object or a string date in a
37000      * valid format (defaults to null).
37001      */
37002     maxValue : null,
37003     /**
37004      * @cfg {String} minText
37005      * The error text to display when the date in the cell is before minValue (defaults to
37006      * 'The date in this field must be after {minValue}').
37007      */
37008     minText : "The date in this field must be equal to or after {0}",
37009     /**
37010      * @cfg {String} maxText
37011      * The error text to display when the date in the cell is after maxValue (defaults to
37012      * 'The date in this field must be before {maxValue}').
37013      */
37014     maxText : "The date in this field must be equal to or before {0}",
37015     /**
37016      * @cfg {String} invalidText
37017      * The error text to display when the date in the field is invalid (defaults to
37018      * '{value} is not a valid date - it must be in the format {format}').
37019      */
37020     invalidText : "{0} is not a valid date - it must be in the format {1}",
37021     /**
37022      * @cfg {String} triggerClass
37023      * An additional CSS class used to style the trigger button.  The trigger will always get the
37024      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
37025      * which displays a calendar icon).
37026      */
37027     triggerClass : 'x-form-date-trigger',
37028     
37029
37030     /**
37031      * @cfg {bool} useIso
37032      * if enabled, then the date field will use a hidden field to store the 
37033      * real value as iso formated date. default (false)
37034      */ 
37035     useIso : false,
37036     /**
37037      * @cfg {String/Object} autoCreate
37038      * A DomHelper element spec, or true for a default element spec (defaults to
37039      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
37040      */ 
37041     // private
37042     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
37043     
37044     // private
37045     hiddenField: false,
37046     
37047     onRender : function(ct, position)
37048     {
37049         Roo.form.DateField.superclass.onRender.call(this, ct, position);
37050         if (this.useIso) {
37051             this.el.dom.removeAttribute('name'); 
37052             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
37053                     'before', true);
37054             this.hiddenField.value = this.formatDate(this.value, 'Y-m-d');
37055             // prevent input submission
37056             this.hiddenName = this.name;
37057         }
37058             
37059             
37060     },
37061     
37062     // private
37063     validateValue : function(value)
37064     {
37065         value = this.formatDate(value);
37066         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
37067             return false;
37068         }
37069         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
37070              return true;
37071         }
37072         var svalue = value;
37073         value = this.parseDate(value);
37074         if(!value){
37075             this.markInvalid(String.format(this.invalidText, svalue, this.format));
37076             return false;
37077         }
37078         var time = value.getTime();
37079         if(this.minValue && time < this.minValue.getTime()){
37080             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
37081             return false;
37082         }
37083         if(this.maxValue && time > this.maxValue.getTime()){
37084             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
37085             return false;
37086         }
37087         if(this.disabledDays){
37088             var day = value.getDay();
37089             for(var i = 0; i < this.disabledDays.length; i++) {
37090                 if(day === this.disabledDays[i]){
37091                     this.markInvalid(this.disabledDaysText);
37092                     return false;
37093                 }
37094             }
37095         }
37096         var fvalue = this.formatDate(value);
37097         if(this.ddMatch && this.ddMatch.test(fvalue)){
37098             this.markInvalid(String.format(this.disabledDatesText, fvalue));
37099             return false;
37100         }
37101         return true;
37102     },
37103
37104     // private
37105     // Provides logic to override the default TriggerField.validateBlur which just returns true
37106     validateBlur : function(){
37107         return !this.menu || !this.menu.isVisible();
37108     },
37109
37110     /**
37111      * Returns the current date value of the date field.
37112      * @return {Date} The date value
37113      */
37114     getValue : function(){
37115         
37116         return  this.hiddenField ? this.hiddenField.value : this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
37117     },
37118
37119     /**
37120      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
37121      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
37122      * (the default format used is "m/d/y").
37123      * <br />Usage:
37124      * <pre><code>
37125 //All of these calls set the same date value (May 4, 2006)
37126
37127 //Pass a date object:
37128 var dt = new Date('5/4/06');
37129 dateField.setValue(dt);
37130
37131 //Pass a date string (default format):
37132 dateField.setValue('5/4/06');
37133
37134 //Pass a date string (custom format):
37135 dateField.format = 'Y-m-d';
37136 dateField.setValue('2006-5-4');
37137 </code></pre>
37138      * @param {String/Date} date The date or valid date string
37139      */
37140     setValue : function(date){
37141         if (this.hiddenField) {
37142             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
37143         }
37144         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
37145     },
37146
37147     // private
37148     parseDate : function(value){
37149         if(!value || value instanceof Date){
37150             return value;
37151         }
37152         var v = Date.parseDate(value, this.format);
37153         if(!v && this.altFormats){
37154             if(!this.altFormatsArray){
37155                 this.altFormatsArray = this.altFormats.split("|");
37156             }
37157             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
37158                 v = Date.parseDate(value, this.altFormatsArray[i]);
37159             }
37160         }
37161         return v;
37162     },
37163
37164     // private
37165     formatDate : function(date, fmt){
37166         return (!date || !(date instanceof Date)) ?
37167                date : date.dateFormat(fmt || this.format);
37168     },
37169
37170     // private
37171     menuListeners : {
37172         select: function(m, d){
37173             this.setValue(d);
37174             this.fireEvent('select', this, d);
37175         },
37176         show : function(){ // retain focus styling
37177             this.onFocus();
37178         },
37179         hide : function(){
37180             this.focus.defer(10, this);
37181             var ml = this.menuListeners;
37182             this.menu.un("select", ml.select,  this);
37183             this.menu.un("show", ml.show,  this);
37184             this.menu.un("hide", ml.hide,  this);
37185         }
37186     },
37187
37188     // private
37189     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
37190     onTriggerClick : function(){
37191         if(this.disabled){
37192             return;
37193         }
37194         if(this.menu == null){
37195             this.menu = new Roo.menu.DateMenu();
37196         }
37197         Roo.apply(this.menu.picker,  {
37198             showClear: this.allowBlank,
37199             minDate : this.minValue,
37200             maxDate : this.maxValue,
37201             disabledDatesRE : this.ddMatch,
37202             disabledDatesText : this.disabledDatesText,
37203             disabledDays : this.disabledDays,
37204             disabledDaysText : this.disabledDaysText,
37205             format : this.format,
37206             minText : String.format(this.minText, this.formatDate(this.minValue)),
37207             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
37208         });
37209         this.menu.on(Roo.apply({}, this.menuListeners, {
37210             scope:this
37211         }));
37212         this.menu.picker.setValue(this.getValue() || new Date());
37213         this.menu.show(this.el, "tl-bl?");
37214     },
37215
37216     beforeBlur : function(){
37217         var v = this.parseDate(this.getRawValue());
37218         if(v){
37219             this.setValue(v);
37220         }
37221     }
37222
37223     /** @cfg {Boolean} grow @hide */
37224     /** @cfg {Number} growMin @hide */
37225     /** @cfg {Number} growMax @hide */
37226     /**
37227      * @hide
37228      * @method autoSize
37229      */
37230 });/*
37231  * Based on:
37232  * Ext JS Library 1.1.1
37233  * Copyright(c) 2006-2007, Ext JS, LLC.
37234  *
37235  * Originally Released Under LGPL - original licence link has changed is not relivant.
37236  *
37237  * Fork - LGPL
37238  * <script type="text/javascript">
37239  */
37240  
37241
37242 /**
37243  * @class Roo.form.ComboBox
37244  * @extends Roo.form.TriggerField
37245  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
37246  * @constructor
37247  * Create a new ComboBox.
37248  * @param {Object} config Configuration options
37249  */
37250 Roo.form.ComboBox = function(config){
37251     Roo.form.ComboBox.superclass.constructor.call(this, config);
37252     this.addEvents({
37253         /**
37254          * @event expand
37255          * Fires when the dropdown list is expanded
37256              * @param {Roo.form.ComboBox} combo This combo box
37257              */
37258         'expand' : true,
37259         /**
37260          * @event collapse
37261          * Fires when the dropdown list is collapsed
37262              * @param {Roo.form.ComboBox} combo This combo box
37263              */
37264         'collapse' : true,
37265         /**
37266          * @event beforeselect
37267          * Fires before a list item is selected. Return false to cancel the selection.
37268              * @param {Roo.form.ComboBox} combo This combo box
37269              * @param {Roo.data.Record} record The data record returned from the underlying store
37270              * @param {Number} index The index of the selected item in the dropdown list
37271              */
37272         'beforeselect' : true,
37273         /**
37274          * @event select
37275          * Fires when a list item is selected
37276              * @param {Roo.form.ComboBox} combo This combo box
37277              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
37278              * @param {Number} index The index of the selected item in the dropdown list
37279              */
37280         'select' : true,
37281         /**
37282          * @event beforequery
37283          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
37284          * The event object passed has these properties:
37285              * @param {Roo.form.ComboBox} combo This combo box
37286              * @param {String} query The query
37287              * @param {Boolean} forceAll true to force "all" query
37288              * @param {Boolean} cancel true to cancel the query
37289              * @param {Object} e The query event object
37290              */
37291         'beforequery': true,
37292          /**
37293          * @event add
37294          * Fires when the 'add' icon is pressed (add a listener to enable add button)
37295              * @param {Roo.form.ComboBox} combo This combo box
37296              */
37297         'add' : true,
37298         /**
37299          * @event edit
37300          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
37301              * @param {Roo.form.ComboBox} combo This combo box
37302              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
37303              */
37304         'edit' : true
37305         
37306         
37307     });
37308     if(this.transform){
37309         this.allowDomMove = false;
37310         var s = Roo.getDom(this.transform);
37311         if(!this.hiddenName){
37312             this.hiddenName = s.name;
37313         }
37314         if(!this.store){
37315             this.mode = 'local';
37316             var d = [], opts = s.options;
37317             for(var i = 0, len = opts.length;i < len; i++){
37318                 var o = opts[i];
37319                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
37320                 if(o.selected) {
37321                     this.value = value;
37322                 }
37323                 d.push([value, o.text]);
37324             }
37325             this.store = new Roo.data.SimpleStore({
37326                 'id': 0,
37327                 fields: ['value', 'text'],
37328                 data : d
37329             });
37330             this.valueField = 'value';
37331             this.displayField = 'text';
37332         }
37333         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
37334         if(!this.lazyRender){
37335             this.target = true;
37336             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
37337             s.parentNode.removeChild(s); // remove it
37338             this.render(this.el.parentNode);
37339         }else{
37340             s.parentNode.removeChild(s); // remove it
37341         }
37342
37343     }
37344     if (this.store) {
37345         this.store = Roo.factory(this.store, Roo.data);
37346     }
37347     
37348     this.selectedIndex = -1;
37349     if(this.mode == 'local'){
37350         if(config.queryDelay === undefined){
37351             this.queryDelay = 10;
37352         }
37353         if(config.minChars === undefined){
37354             this.minChars = 0;
37355         }
37356     }
37357 };
37358
37359 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
37360     /**
37361      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
37362      */
37363     /**
37364      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
37365      * rendering into an Roo.Editor, defaults to false)
37366      */
37367     /**
37368      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
37369      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
37370      */
37371     /**
37372      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
37373      */
37374     /**
37375      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
37376      * the dropdown list (defaults to undefined, with no header element)
37377      */
37378
37379      /**
37380      * @cfg {String/Roo.Template} tpl The template to use to render the output
37381      */
37382      
37383     // private
37384     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
37385     /**
37386      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
37387      */
37388     listWidth: undefined,
37389     /**
37390      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
37391      * mode = 'remote' or 'text' if mode = 'local')
37392      */
37393     displayField: undefined,
37394     /**
37395      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
37396      * mode = 'remote' or 'value' if mode = 'local'). 
37397      * Note: use of a valueField requires the user make a selection
37398      * in order for a value to be mapped.
37399      */
37400     valueField: undefined,
37401     /**
37402      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
37403      * field's data value (defaults to the underlying DOM element's name)
37404      */
37405     hiddenName: undefined,
37406     /**
37407      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
37408      */
37409     listClass: '',
37410     /**
37411      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
37412      */
37413     selectedClass: 'x-combo-selected',
37414     /**
37415      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
37416      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
37417      * which displays a downward arrow icon).
37418      */
37419     triggerClass : 'x-form-arrow-trigger',
37420     /**
37421      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
37422      */
37423     shadow:'sides',
37424     /**
37425      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
37426      * anchor positions (defaults to 'tl-bl')
37427      */
37428     listAlign: 'tl-bl?',
37429     /**
37430      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
37431      */
37432     maxHeight: 300,
37433     /**
37434      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
37435      * query specified by the allQuery config option (defaults to 'query')
37436      */
37437     triggerAction: 'query',
37438     /**
37439      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
37440      * (defaults to 4, does not apply if editable = false)
37441      */
37442     minChars : 4,
37443     /**
37444      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
37445      * delay (typeAheadDelay) if it matches a known value (defaults to false)
37446      */
37447     typeAhead: false,
37448     /**
37449      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
37450      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
37451      */
37452     queryDelay: 500,
37453     /**
37454      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
37455      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
37456      */
37457     pageSize: 0,
37458     /**
37459      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
37460      * when editable = true (defaults to false)
37461      */
37462     selectOnFocus:false,
37463     /**
37464      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
37465      */
37466     queryParam: 'query',
37467     /**
37468      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
37469      * when mode = 'remote' (defaults to 'Loading...')
37470      */
37471     loadingText: 'Loading...',
37472     /**
37473      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
37474      */
37475     resizable: false,
37476     /**
37477      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
37478      */
37479     handleHeight : 8,
37480     /**
37481      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
37482      * traditional select (defaults to true)
37483      */
37484     editable: true,
37485     /**
37486      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
37487      */
37488     allQuery: '',
37489     /**
37490      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
37491      */
37492     mode: 'remote',
37493     /**
37494      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
37495      * listWidth has a higher value)
37496      */
37497     minListWidth : 70,
37498     /**
37499      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
37500      * allow the user to set arbitrary text into the field (defaults to false)
37501      */
37502     forceSelection:false,
37503     /**
37504      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
37505      * if typeAhead = true (defaults to 250)
37506      */
37507     typeAheadDelay : 250,
37508     /**
37509      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
37510      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
37511      */
37512     valueNotFoundText : undefined,
37513     /**
37514      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
37515      */
37516     blockFocus : false,
37517     
37518     /**
37519      * @cfg {Boolean} disableClear Disable showing of clear button.
37520      */
37521     disableClear : false,
37522     /**
37523      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
37524      */
37525     alwaysQuery : false,
37526     
37527     //private
37528     addicon : false,
37529     editicon: false,
37530     
37531     
37532     // private
37533     onRender : function(ct, position){
37534         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
37535         if(this.hiddenName){
37536             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
37537                     'before', true);
37538             this.hiddenField.value =
37539                 this.hiddenValue !== undefined ? this.hiddenValue :
37540                 this.value !== undefined ? this.value : '';
37541
37542             // prevent input submission
37543             this.el.dom.removeAttribute('name');
37544         }
37545         if(Roo.isGecko){
37546             this.el.dom.setAttribute('autocomplete', 'off');
37547         }
37548
37549         var cls = 'x-combo-list';
37550
37551         this.list = new Roo.Layer({
37552             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
37553         });
37554
37555         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
37556         this.list.setWidth(lw);
37557         this.list.swallowEvent('mousewheel');
37558         this.assetHeight = 0;
37559
37560         if(this.title){
37561             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
37562             this.assetHeight += this.header.getHeight();
37563         }
37564
37565         this.innerList = this.list.createChild({cls:cls+'-inner'});
37566         this.innerList.on('mouseover', this.onViewOver, this);
37567         this.innerList.on('mousemove', this.onViewMove, this);
37568         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
37569         
37570         if(this.allowBlank && !this.pageSize && !this.disableClear){
37571             this.footer = this.list.createChild({cls:cls+'-ft'});
37572             this.pageTb = new Roo.Toolbar(this.footer);
37573            
37574         }
37575         if(this.pageSize){
37576             this.footer = this.list.createChild({cls:cls+'-ft'});
37577             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
37578                     {pageSize: this.pageSize});
37579             
37580         }
37581         
37582         if (this.pageTb && this.allowBlank && !this.disableClear) {
37583             var _this = this;
37584             this.pageTb.add(new Roo.Toolbar.Fill(), {
37585                 cls: 'x-btn-icon x-btn-clear',
37586                 text: '&#160;',
37587                 handler: function()
37588                 {
37589                     _this.collapse();
37590                     _this.clearValue();
37591                     _this.onSelect(false, -1);
37592                 }
37593             });
37594         }
37595         if (this.footer) {
37596             this.assetHeight += this.footer.getHeight();
37597         }
37598         
37599
37600         if(!this.tpl){
37601             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
37602         }
37603
37604         this.view = new Roo.View(this.innerList, this.tpl, {
37605             singleSelect:true, store: this.store, selectedClass: this.selectedClass
37606         });
37607
37608         this.view.on('click', this.onViewClick, this);
37609
37610         this.store.on('beforeload', this.onBeforeLoad, this);
37611         this.store.on('load', this.onLoad, this);
37612         this.store.on('loadexception', this.collapse, this);
37613
37614         if(this.resizable){
37615             this.resizer = new Roo.Resizable(this.list,  {
37616                pinned:true, handles:'se'
37617             });
37618             this.resizer.on('resize', function(r, w, h){
37619                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
37620                 this.listWidth = w;
37621                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
37622                 this.restrictHeight();
37623             }, this);
37624             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
37625         }
37626         if(!this.editable){
37627             this.editable = true;
37628             this.setEditable(false);
37629         }  
37630         
37631         
37632         if (typeof(this.events.add.listeners) != 'undefined') {
37633             
37634             this.addicon = this.wrap.createChild(
37635                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
37636        
37637             this.addicon.on('click', function(e) {
37638                 this.fireEvent('add', this);
37639             }, this);
37640         }
37641         if (typeof(this.events.edit.listeners) != 'undefined') {
37642             
37643             this.editicon = this.wrap.createChild(
37644                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
37645             if (this.addicon) {
37646                 this.editicon.setStyle('margin-left', '40px');
37647             }
37648             this.editicon.on('click', function(e) {
37649                 
37650                 // we fire even  if inothing is selected..
37651                 this.fireEvent('edit', this, this.lastData );
37652                 
37653             }, this);
37654         }
37655         
37656         
37657         
37658     },
37659
37660     // private
37661     initEvents : function(){
37662         Roo.form.ComboBox.superclass.initEvents.call(this);
37663
37664         this.keyNav = new Roo.KeyNav(this.el, {
37665             "up" : function(e){
37666                 this.inKeyMode = true;
37667                 this.selectPrev();
37668             },
37669
37670             "down" : function(e){
37671                 if(!this.isExpanded()){
37672                     this.onTriggerClick();
37673                 }else{
37674                     this.inKeyMode = true;
37675                     this.selectNext();
37676                 }
37677             },
37678
37679             "enter" : function(e){
37680                 this.onViewClick();
37681                 //return true;
37682             },
37683
37684             "esc" : function(e){
37685                 this.collapse();
37686             },
37687
37688             "tab" : function(e){
37689                 this.onViewClick(false);
37690                 this.fireEvent("specialkey", this, e);
37691                 return true;
37692             },
37693
37694             scope : this,
37695
37696             doRelay : function(foo, bar, hname){
37697                 if(hname == 'down' || this.scope.isExpanded()){
37698                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
37699                 }
37700                 return true;
37701             },
37702
37703             forceKeyDown: true
37704         });
37705         this.queryDelay = Math.max(this.queryDelay || 10,
37706                 this.mode == 'local' ? 10 : 250);
37707         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
37708         if(this.typeAhead){
37709             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
37710         }
37711         if(this.editable !== false){
37712             this.el.on("keyup", this.onKeyUp, this);
37713         }
37714         if(this.forceSelection){
37715             this.on('blur', this.doForce, this);
37716         }
37717     },
37718
37719     onDestroy : function(){
37720         if(this.view){
37721             this.view.setStore(null);
37722             this.view.el.removeAllListeners();
37723             this.view.el.remove();
37724             this.view.purgeListeners();
37725         }
37726         if(this.list){
37727             this.list.destroy();
37728         }
37729         if(this.store){
37730             this.store.un('beforeload', this.onBeforeLoad, this);
37731             this.store.un('load', this.onLoad, this);
37732             this.store.un('loadexception', this.collapse, this);
37733         }
37734         Roo.form.ComboBox.superclass.onDestroy.call(this);
37735     },
37736
37737     // private
37738     fireKey : function(e){
37739         if(e.isNavKeyPress() && !this.list.isVisible()){
37740             this.fireEvent("specialkey", this, e);
37741         }
37742     },
37743
37744     // private
37745     onResize: function(w, h){
37746         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
37747         
37748         if(typeof w != 'number'){
37749             // we do not handle it!?!?
37750             return;
37751         }
37752         var tw = this.trigger.getWidth();
37753         tw += this.addicon ? this.addicon.getWidth() : 0;
37754         tw += this.editicon ? this.editicon.getWidth() : 0;
37755         var x = w - tw;
37756         this.el.setWidth( this.adjustWidth('input', x));
37757             
37758         this.trigger.setStyle('left', x+'px');
37759         
37760         if(this.list && this.listWidth === undefined){
37761             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
37762             this.list.setWidth(lw);
37763             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
37764         }
37765         
37766     
37767         
37768     },
37769
37770     /**
37771      * Allow or prevent the user from directly editing the field text.  If false is passed,
37772      * the user will only be able to select from the items defined in the dropdown list.  This method
37773      * is the runtime equivalent of setting the 'editable' config option at config time.
37774      * @param {Boolean} value True to allow the user to directly edit the field text
37775      */
37776     setEditable : function(value){
37777         if(value == this.editable){
37778             return;
37779         }
37780         this.editable = value;
37781         if(!value){
37782             this.el.dom.setAttribute('readOnly', true);
37783             this.el.on('mousedown', this.onTriggerClick,  this);
37784             this.el.addClass('x-combo-noedit');
37785         }else{
37786             this.el.dom.setAttribute('readOnly', false);
37787             this.el.un('mousedown', this.onTriggerClick,  this);
37788             this.el.removeClass('x-combo-noedit');
37789         }
37790     },
37791
37792     // private
37793     onBeforeLoad : function(){
37794         if(!this.hasFocus){
37795             return;
37796         }
37797         this.innerList.update(this.loadingText ?
37798                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
37799         this.restrictHeight();
37800         this.selectedIndex = -1;
37801     },
37802
37803     // private
37804     onLoad : function(){
37805         if(!this.hasFocus){
37806             return;
37807         }
37808         if(this.store.getCount() > 0){
37809             this.expand();
37810             this.restrictHeight();
37811             if(this.lastQuery == this.allQuery){
37812                 if(this.editable){
37813                     this.el.dom.select();
37814                 }
37815                 if(!this.selectByValue(this.value, true)){
37816                     this.select(0, true);
37817                 }
37818             }else{
37819                 this.selectNext();
37820                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
37821                     this.taTask.delay(this.typeAheadDelay);
37822                 }
37823             }
37824         }else{
37825             this.onEmptyResults();
37826         }
37827         //this.el.focus();
37828     },
37829
37830     // private
37831     onTypeAhead : function(){
37832         if(this.store.getCount() > 0){
37833             var r = this.store.getAt(0);
37834             var newValue = r.data[this.displayField];
37835             var len = newValue.length;
37836             var selStart = this.getRawValue().length;
37837             if(selStart != len){
37838                 this.setRawValue(newValue);
37839                 this.selectText(selStart, newValue.length);
37840             }
37841         }
37842     },
37843
37844     // private
37845     onSelect : function(record, index){
37846         if(this.fireEvent('beforeselect', this, record, index) !== false){
37847             this.setFromData(index > -1 ? record.data : false);
37848             this.collapse();
37849             this.fireEvent('select', this, record, index);
37850         }
37851     },
37852
37853     /**
37854      * Returns the currently selected field value or empty string if no value is set.
37855      * @return {String} value The selected value
37856      */
37857     getValue : function(){
37858         if(this.valueField){
37859             return typeof this.value != 'undefined' ? this.value : '';
37860         }else{
37861             return Roo.form.ComboBox.superclass.getValue.call(this);
37862         }
37863     },
37864
37865     /**
37866      * Clears any text/value currently set in the field
37867      */
37868     clearValue : function(){
37869         if(this.hiddenField){
37870             this.hiddenField.value = '';
37871         }
37872         this.value = '';
37873         this.setRawValue('');
37874         this.lastSelectionText = '';
37875         this.applyEmptyText();
37876     },
37877
37878     /**
37879      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
37880      * will be displayed in the field.  If the value does not match the data value of an existing item,
37881      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
37882      * Otherwise the field will be blank (although the value will still be set).
37883      * @param {String} value The value to match
37884      */
37885     setValue : function(v){
37886         var text = v;
37887         if(this.valueField){
37888             var r = this.findRecord(this.valueField, v);
37889             if(r){
37890                 text = r.data[this.displayField];
37891             }else if(this.valueNotFoundText !== undefined){
37892                 text = this.valueNotFoundText;
37893             }
37894         }
37895         this.lastSelectionText = text;
37896         if(this.hiddenField){
37897             this.hiddenField.value = v;
37898         }
37899         Roo.form.ComboBox.superclass.setValue.call(this, text);
37900         this.value = v;
37901     },
37902     /**
37903      * @property {Object} the last set data for the element
37904      */
37905     
37906     lastData : false,
37907     /**
37908      * Sets the value of the field based on a object which is related to the record format for the store.
37909      * @param {Object} value the value to set as. or false on reset?
37910      */
37911     setFromData : function(o){
37912         var dv = ''; // display value
37913         var vv = ''; // value value..
37914         this.lastData = o;
37915         if (this.displayField) {
37916             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
37917         } else {
37918             // this is an error condition!!!
37919             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
37920         }
37921         
37922         if(this.valueField){
37923             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
37924         }
37925         if(this.hiddenField){
37926             this.hiddenField.value = vv;
37927             
37928             this.lastSelectionText = dv;
37929             Roo.form.ComboBox.superclass.setValue.call(this, dv);
37930             this.value = vv;
37931             return;
37932         }
37933         // no hidden field.. - we store the value in 'value', but still display
37934         // display field!!!!
37935         this.lastSelectionText = dv;
37936         Roo.form.ComboBox.superclass.setValue.call(this, dv);
37937         this.value = vv;
37938         
37939         
37940     },
37941     // private
37942     reset : function(){
37943         // overridden so that last data is reset..
37944         this.setValue(this.originalValue);
37945         this.clearInvalid();
37946         this.lastData = false;
37947     },
37948     // private
37949     findRecord : function(prop, value){
37950         var record;
37951         if(this.store.getCount() > 0){
37952             this.store.each(function(r){
37953                 if(r.data[prop] == value){
37954                     record = r;
37955                     return false;
37956                 }
37957             });
37958         }
37959         return record;
37960     },
37961
37962     // private
37963     onViewMove : function(e, t){
37964         this.inKeyMode = false;
37965     },
37966
37967     // private
37968     onViewOver : function(e, t){
37969         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
37970             return;
37971         }
37972         var item = this.view.findItemFromChild(t);
37973         if(item){
37974             var index = this.view.indexOf(item);
37975             this.select(index, false);
37976         }
37977     },
37978
37979     // private
37980     onViewClick : function(doFocus)
37981     {
37982         var index = this.view.getSelectedIndexes()[0];
37983         var r = this.store.getAt(index);
37984         if(r){
37985             this.onSelect(r, index);
37986         }
37987         if(doFocus !== false && !this.blockFocus){
37988             this.el.focus();
37989         }
37990     },
37991
37992     // private
37993     restrictHeight : function(){
37994         this.innerList.dom.style.height = '';
37995         var inner = this.innerList.dom;
37996         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
37997         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
37998         this.list.beginUpdate();
37999         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
38000         this.list.alignTo(this.el, this.listAlign);
38001         this.list.endUpdate();
38002     },
38003
38004     // private
38005     onEmptyResults : function(){
38006         this.collapse();
38007     },
38008
38009     /**
38010      * Returns true if the dropdown list is expanded, else false.
38011      */
38012     isExpanded : function(){
38013         return this.list.isVisible();
38014     },
38015
38016     /**
38017      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
38018      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
38019      * @param {String} value The data value of the item to select
38020      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
38021      * selected item if it is not currently in view (defaults to true)
38022      * @return {Boolean} True if the value matched an item in the list, else false
38023      */
38024     selectByValue : function(v, scrollIntoView){
38025         if(v !== undefined && v !== null){
38026             var r = this.findRecord(this.valueField || this.displayField, v);
38027             if(r){
38028                 this.select(this.store.indexOf(r), scrollIntoView);
38029                 return true;
38030             }
38031         }
38032         return false;
38033     },
38034
38035     /**
38036      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
38037      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
38038      * @param {Number} index The zero-based index of the list item to select
38039      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
38040      * selected item if it is not currently in view (defaults to true)
38041      */
38042     select : function(index, scrollIntoView){
38043         this.selectedIndex = index;
38044         this.view.select(index);
38045         if(scrollIntoView !== false){
38046             var el = this.view.getNode(index);
38047             if(el){
38048                 this.innerList.scrollChildIntoView(el, false);
38049             }
38050         }
38051     },
38052
38053     // private
38054     selectNext : function(){
38055         var ct = this.store.getCount();
38056         if(ct > 0){
38057             if(this.selectedIndex == -1){
38058                 this.select(0);
38059             }else if(this.selectedIndex < ct-1){
38060                 this.select(this.selectedIndex+1);
38061             }
38062         }
38063     },
38064
38065     // private
38066     selectPrev : function(){
38067         var ct = this.store.getCount();
38068         if(ct > 0){
38069             if(this.selectedIndex == -1){
38070                 this.select(0);
38071             }else if(this.selectedIndex != 0){
38072                 this.select(this.selectedIndex-1);
38073             }
38074         }
38075     },
38076
38077     // private
38078     onKeyUp : function(e){
38079         if(this.editable !== false && !e.isSpecialKey()){
38080             this.lastKey = e.getKey();
38081             this.dqTask.delay(this.queryDelay);
38082         }
38083     },
38084
38085     // private
38086     validateBlur : function(){
38087         return !this.list || !this.list.isVisible();   
38088     },
38089
38090     // private
38091     initQuery : function(){
38092         this.doQuery(this.getRawValue());
38093     },
38094
38095     // private
38096     doForce : function(){
38097         if(this.el.dom.value.length > 0){
38098             this.el.dom.value =
38099                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
38100             this.applyEmptyText();
38101         }
38102     },
38103
38104     /**
38105      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
38106      * query allowing the query action to be canceled if needed.
38107      * @param {String} query The SQL query to execute
38108      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
38109      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
38110      * saved in the current store (defaults to false)
38111      */
38112     doQuery : function(q, forceAll){
38113         if(q === undefined || q === null){
38114             q = '';
38115         }
38116         var qe = {
38117             query: q,
38118             forceAll: forceAll,
38119             combo: this,
38120             cancel:false
38121         };
38122         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
38123             return false;
38124         }
38125         q = qe.query;
38126         forceAll = qe.forceAll;
38127         if(forceAll === true || (q.length >= this.minChars)){
38128             if(this.lastQuery != q || this.alwaysQuery){
38129                 this.lastQuery = q;
38130                 if(this.mode == 'local'){
38131                     this.selectedIndex = -1;
38132                     if(forceAll){
38133                         this.store.clearFilter();
38134                     }else{
38135                         this.store.filter(this.displayField, q);
38136                     }
38137                     this.onLoad();
38138                 }else{
38139                     this.store.baseParams[this.queryParam] = q;
38140                     this.store.load({
38141                         params: this.getParams(q)
38142                     });
38143                     this.expand();
38144                 }
38145             }else{
38146                 this.selectedIndex = -1;
38147                 this.onLoad();   
38148             }
38149         }
38150     },
38151
38152     // private
38153     getParams : function(q){
38154         var p = {};
38155         //p[this.queryParam] = q;
38156         if(this.pageSize){
38157             p.start = 0;
38158             p.limit = this.pageSize;
38159         }
38160         return p;
38161     },
38162
38163     /**
38164      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
38165      */
38166     collapse : function(){
38167         if(!this.isExpanded()){
38168             return;
38169         }
38170         this.list.hide();
38171         Roo.get(document).un('mousedown', this.collapseIf, this);
38172         Roo.get(document).un('mousewheel', this.collapseIf, this);
38173         if (!this.editable) {
38174             Roo.get(document).un('keydown', this.listKeyPress, this);
38175         }
38176         this.fireEvent('collapse', this);
38177     },
38178
38179     // private
38180     collapseIf : function(e){
38181         if(!e.within(this.wrap) && !e.within(this.list)){
38182             this.collapse();
38183         }
38184     },
38185
38186     /**
38187      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
38188      */
38189     expand : function(){
38190         if(this.isExpanded() || !this.hasFocus){
38191             return;
38192         }
38193         this.list.alignTo(this.el, this.listAlign);
38194         this.list.show();
38195         Roo.get(document).on('mousedown', this.collapseIf, this);
38196         Roo.get(document).on('mousewheel', this.collapseIf, this);
38197         if (!this.editable) {
38198             Roo.get(document).on('keydown', this.listKeyPress, this);
38199         }
38200         
38201         this.fireEvent('expand', this);
38202     },
38203
38204     // private
38205     // Implements the default empty TriggerField.onTriggerClick function
38206     onTriggerClick : function(){
38207         if(this.disabled){
38208             return;
38209         }
38210         if(this.isExpanded()){
38211             this.collapse();
38212             if (!this.blockFocus) {
38213                 this.el.focus();
38214             }
38215             
38216         }else {
38217             this.hasFocus = true;
38218             if(this.triggerAction == 'all') {
38219                 this.doQuery(this.allQuery, true);
38220             } else {
38221                 this.doQuery(this.getRawValue());
38222             }
38223             if (!this.blockFocus) {
38224                 this.el.focus();
38225             }
38226         }
38227     },
38228     listKeyPress : function(e)
38229     {
38230         //Roo.log('listkeypress');
38231         // scroll to first matching element based on key pres..
38232         if (e.isSpecialKey()) {
38233             return false;
38234         }
38235         var k = String.fromCharCode(e.getKey()).toUpperCase();
38236         //Roo.log(k);
38237         var match  = false;
38238         var csel = this.view.getSelectedNodes();
38239         var cselitem = false;
38240         if (csel.length) {
38241             var ix = this.view.indexOf(csel[0]);
38242             cselitem  = this.store.getAt(ix);
38243             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
38244                 cselitem = false;
38245             }
38246             
38247         }
38248         
38249         this.store.each(function(v) { 
38250             if (cselitem) {
38251                 // start at existing selection.
38252                 if (cselitem.id == v.id) {
38253                     cselitem = false;
38254                 }
38255                 return;
38256             }
38257                 
38258             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
38259                 match = this.store.indexOf(v);
38260                 return false;
38261             }
38262         }, this);
38263         
38264         if (match === false) {
38265             return true; // no more action?
38266         }
38267         // scroll to?
38268         this.view.select(match);
38269         var sn = Roo.get(this.view.getSelectedNodes()[0])
38270         sn.scrollIntoView(sn.dom.parentNode, false);
38271     }
38272
38273     /** 
38274     * @cfg {Boolean} grow 
38275     * @hide 
38276     */
38277     /** 
38278     * @cfg {Number} growMin 
38279     * @hide 
38280     */
38281     /** 
38282     * @cfg {Number} growMax 
38283     * @hide 
38284     */
38285     /**
38286      * @hide
38287      * @method autoSize
38288      */
38289 });/*
38290  * Based on:
38291  * Ext JS Library 1.1.1
38292  * Copyright(c) 2006-2007, Ext JS, LLC.
38293  *
38294  * Originally Released Under LGPL - original licence link has changed is not relivant.
38295  *
38296  * Fork - LGPL
38297  * <script type="text/javascript">
38298  */
38299 /**
38300  * @class Roo.form.Checkbox
38301  * @extends Roo.form.Field
38302  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
38303  * @constructor
38304  * Creates a new Checkbox
38305  * @param {Object} config Configuration options
38306  */
38307 Roo.form.Checkbox = function(config){
38308     Roo.form.Checkbox.superclass.constructor.call(this, config);
38309     this.addEvents({
38310         /**
38311          * @event check
38312          * Fires when the checkbox is checked or unchecked.
38313              * @param {Roo.form.Checkbox} this This checkbox
38314              * @param {Boolean} checked The new checked value
38315              */
38316         check : true
38317     });
38318 };
38319
38320 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
38321     /**
38322      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
38323      */
38324     focusClass : undefined,
38325     /**
38326      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
38327      */
38328     fieldClass: "x-form-field",
38329     /**
38330      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
38331      */
38332     checked: false,
38333     /**
38334      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38335      * {tag: "input", type: "checkbox", autocomplete: "off"})
38336      */
38337     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
38338     /**
38339      * @cfg {String} boxLabel The text that appears beside the checkbox
38340      */
38341     boxLabel : "",
38342     /**
38343      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
38344      */  
38345     inputValue : '1',
38346     /**
38347      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
38348      */
38349      valueOff: '0', // value when not checked..
38350
38351     actionMode : 'viewEl', 
38352     //
38353     // private
38354     itemCls : 'x-menu-check-item x-form-item',
38355     groupClass : 'x-menu-group-item',
38356     inputType : 'hidden',
38357     
38358     
38359     inSetChecked: false, // check that we are not calling self...
38360     
38361     inputElement: false, // real input element?
38362     basedOn: false, // ????
38363     
38364     isFormField: true, // not sure where this is needed!!!!
38365
38366     onResize : function(){
38367         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
38368         if(!this.boxLabel){
38369             this.el.alignTo(this.wrap, 'c-c');
38370         }
38371     },
38372
38373     initEvents : function(){
38374         Roo.form.Checkbox.superclass.initEvents.call(this);
38375         this.el.on("click", this.onClick,  this);
38376         this.el.on("change", this.onClick,  this);
38377     },
38378
38379
38380     getResizeEl : function(){
38381         return this.wrap;
38382     },
38383
38384     getPositionEl : function(){
38385         return this.wrap;
38386     },
38387
38388     // private
38389     onRender : function(ct, position){
38390         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
38391         /*
38392         if(this.inputValue !== undefined){
38393             this.el.dom.value = this.inputValue;
38394         }
38395         */
38396         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
38397         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
38398         var viewEl = this.wrap.createChild({ 
38399             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
38400         this.viewEl = viewEl;   
38401         this.wrap.on('click', this.onClick,  this); 
38402         
38403         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
38404         this.el.on('propertychange', this.setFromHidden,  this);  //ie
38405         
38406         
38407         
38408         if(this.boxLabel){
38409             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
38410         //    viewEl.on('click', this.onClick,  this); 
38411         }
38412         //if(this.checked){
38413             this.setChecked(this.checked);
38414         //}else{
38415             //this.checked = this.el.dom;
38416         //}
38417
38418     },
38419
38420     // private
38421     initValue : Roo.emptyFn,
38422
38423     /**
38424      * Returns the checked state of the checkbox.
38425      * @return {Boolean} True if checked, else false
38426      */
38427     getValue : function(){
38428         if(this.el){
38429             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
38430         }
38431         return this.valueOff;
38432         
38433     },
38434
38435         // private
38436     onClick : function(){ 
38437         this.setChecked(!this.checked);
38438
38439         //if(this.el.dom.checked != this.checked){
38440         //    this.setValue(this.el.dom.checked);
38441        // }
38442     },
38443
38444     /**
38445      * Sets the checked state of the checkbox.
38446      * On is always based on a string comparison between inputValue and the param.
38447      * @param {Boolean/String} value - the value to set 
38448      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
38449      */
38450     setValue : function(v,suppressEvent){
38451         
38452         
38453         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
38454         //if(this.el && this.el.dom){
38455         //    this.el.dom.checked = this.checked;
38456         //    this.el.dom.defaultChecked = this.checked;
38457         //}
38458         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
38459         //this.fireEvent("check", this, this.checked);
38460     },
38461     // private..
38462     setChecked : function(state,suppressEvent)
38463     {
38464         if (this.inSetChecked) {
38465             this.checked = state;
38466             return;
38467         }
38468         
38469     
38470         if(this.wrap){
38471             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
38472         }
38473         this.checked = state;
38474         if(suppressEvent !== true){
38475             this.fireEvent('check', this, state);
38476         }
38477         this.inSetChecked = true;
38478         this.el.dom.value = state ? this.inputValue : this.valueOff;
38479         this.inSetChecked = false;
38480         
38481     },
38482     // handle setting of hidden value by some other method!!?!?
38483     setFromHidden: function()
38484     {
38485         if(!this.el){
38486             return;
38487         }
38488         //console.log("SET FROM HIDDEN");
38489         //alert('setFrom hidden');
38490         this.setValue(this.el.dom.value);
38491     },
38492     
38493     onDestroy : function()
38494     {
38495         if(this.viewEl){
38496             Roo.get(this.viewEl).remove();
38497         }
38498          
38499         Roo.form.Checkbox.superclass.onDestroy.call(this);
38500     }
38501
38502 });/*
38503  * Based on:
38504  * Ext JS Library 1.1.1
38505  * Copyright(c) 2006-2007, Ext JS, LLC.
38506  *
38507  * Originally Released Under LGPL - original licence link has changed is not relivant.
38508  *
38509  * Fork - LGPL
38510  * <script type="text/javascript">
38511  */
38512  
38513 /**
38514  * @class Roo.form.Radio
38515  * @extends Roo.form.Checkbox
38516  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
38517  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
38518  * @constructor
38519  * Creates a new Radio
38520  * @param {Object} config Configuration options
38521  */
38522 Roo.form.Radio = function(){
38523     Roo.form.Radio.superclass.constructor.apply(this, arguments);
38524 };
38525 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
38526     inputType: 'radio',
38527
38528     /**
38529      * If this radio is part of a group, it will return the selected value
38530      * @return {String}
38531      */
38532     getGroupValue : function(){
38533         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
38534     }
38535 });//<script type="text/javascript">
38536
38537 /*
38538  * Ext JS Library 1.1.1
38539  * Copyright(c) 2006-2007, Ext JS, LLC.
38540  * licensing@extjs.com
38541  * 
38542  * http://www.extjs.com/license
38543  */
38544  
38545  /*
38546   * 
38547   * Known bugs:
38548   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
38549   * - IE ? - no idea how much works there.
38550   * 
38551   * 
38552   * 
38553   */
38554  
38555
38556 /**
38557  * @class Ext.form.HtmlEditor
38558  * @extends Ext.form.Field
38559  * Provides a lightweight HTML Editor component.
38560  * WARNING - THIS CURRENTlY ONLY WORKS ON FIREFOX - USE FCKeditor for a cross platform version
38561  * 
38562  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
38563  * supported by this editor.</b><br/><br/>
38564  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
38565  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
38566  */
38567 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
38568       /**
38569      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
38570      */
38571     toolbars : false,
38572     /**
38573      * @cfg {String} createLinkText The default text for the create link prompt
38574      */
38575     createLinkText : 'Please enter the URL for the link:',
38576     /**
38577      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
38578      */
38579     defaultLinkValue : 'http:/'+'/',
38580    
38581      /**
38582      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
38583      *                        Roo.resizable.
38584      */
38585     resizable : false,
38586      /**
38587      * @cfg {Number} height (in pixels)
38588      */   
38589     height: 300,
38590    /**
38591      * @cfg {Number} width (in pixels)
38592      */   
38593     width: 500,
38594     
38595     /**
38596      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
38597      * 
38598      */
38599     stylesheets: false,
38600     
38601     // id of frame..
38602     frameId: false,
38603     
38604     // private properties
38605     validationEvent : false,
38606     deferHeight: true,
38607     initialized : false,
38608     activated : false,
38609     sourceEditMode : false,
38610     onFocus : Roo.emptyFn,
38611     iframePad:3,
38612     hideMode:'offsets',
38613     
38614     defaultAutoCreate : { // modified by initCompnoent..
38615         tag: "textarea",
38616         style:"width:500px;height:300px;",
38617         autocomplete: "off"
38618     },
38619
38620     // private
38621     initComponent : function(){
38622         this.addEvents({
38623             /**
38624              * @event initialize
38625              * Fires when the editor is fully initialized (including the iframe)
38626              * @param {HtmlEditor} this
38627              */
38628             initialize: true,
38629             /**
38630              * @event activate
38631              * Fires when the editor is first receives the focus. Any insertion must wait
38632              * until after this event.
38633              * @param {HtmlEditor} this
38634              */
38635             activate: true,
38636              /**
38637              * @event beforesync
38638              * Fires before the textarea is updated with content from the editor iframe. Return false
38639              * to cancel the sync.
38640              * @param {HtmlEditor} this
38641              * @param {String} html
38642              */
38643             beforesync: true,
38644              /**
38645              * @event beforepush
38646              * Fires before the iframe editor is updated with content from the textarea. Return false
38647              * to cancel the push.
38648              * @param {HtmlEditor} this
38649              * @param {String} html
38650              */
38651             beforepush: true,
38652              /**
38653              * @event sync
38654              * Fires when the textarea is updated with content from the editor iframe.
38655              * @param {HtmlEditor} this
38656              * @param {String} html
38657              */
38658             sync: true,
38659              /**
38660              * @event push
38661              * Fires when the iframe editor is updated with content from the textarea.
38662              * @param {HtmlEditor} this
38663              * @param {String} html
38664              */
38665             push: true,
38666              /**
38667              * @event editmodechange
38668              * Fires when the editor switches edit modes
38669              * @param {HtmlEditor} this
38670              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
38671              */
38672             editmodechange: true,
38673             /**
38674              * @event editorevent
38675              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
38676              * @param {HtmlEditor} this
38677              */
38678             editorevent: true
38679         });
38680         this.defaultAutoCreate =  {
38681             tag: "textarea",
38682             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
38683             autocomplete: "off"
38684         };
38685     },
38686
38687     /**
38688      * Protected method that will not generally be called directly. It
38689      * is called when the editor creates its toolbar. Override this method if you need to
38690      * add custom toolbar buttons.
38691      * @param {HtmlEditor} editor
38692      */
38693     createToolbar : function(editor){
38694         if (!editor.toolbars || !editor.toolbars.length) {
38695             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
38696         }
38697         
38698         for (var i =0 ; i < editor.toolbars.length;i++) {
38699             editor.toolbars[i] = Roo.factory(editor.toolbars[i], Roo.form.HtmlEditor);
38700             editor.toolbars[i].init(editor);
38701         }
38702          
38703         
38704     },
38705
38706     /**
38707      * Protected method that will not generally be called directly. It
38708      * is called when the editor initializes the iframe with HTML contents. Override this method if you
38709      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
38710      */
38711     getDocMarkup : function(){
38712         // body styles..
38713         var st = '';
38714         if (this.stylesheets === false) {
38715             
38716             Roo.get(document.head).select('style').each(function(node) {
38717                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
38718             });
38719             
38720             Roo.get(document.head).select('link').each(function(node) { 
38721                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
38722             });
38723             
38724         } else if (!this.stylesheets.length) {
38725                 // simple..
38726                 st = '<style type="text/css">' +
38727                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
38728                    '</style>';
38729         } else {
38730             Roo.each(this.stylesheets, function(s) {
38731                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
38732             });
38733             
38734         }
38735         
38736         return '<html><head>' + st  +
38737             //<style type="text/css">' +
38738             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
38739             //'</style>' +
38740             ' </head><body></body></html>';
38741     },
38742
38743     // private
38744     onRender : function(ct, position)
38745     {
38746         var _t = this;
38747         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
38748         this.el.dom.style.border = '0 none';
38749         this.el.dom.setAttribute('tabIndex', -1);
38750         this.el.addClass('x-hidden');
38751         if(Roo.isIE){ // fix IE 1px bogus margin
38752             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
38753         }
38754         this.wrap = this.el.wrap({
38755             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
38756         });
38757         
38758         if (this.resizable) {
38759             this.resizeEl = new Roo.Resizable(this.wrap, {
38760                 pinned : true,
38761                 wrap: true,
38762                 dynamic : true,
38763                 minHeight : this.height,
38764                 height: this.height,
38765                 handles : this.resizable,
38766                 width: this.width,
38767                 listeners : {
38768                     resize : function(r, w, h) {
38769                         _t.onResize(w,h); // -something
38770                     }
38771                 }
38772             });
38773             
38774         }
38775
38776         this.frameId = Roo.id();
38777         
38778         this.createToolbar(this);
38779         
38780       
38781         
38782         var iframe = this.wrap.createChild({
38783             tag: 'iframe',
38784             id: this.frameId,
38785             name: this.frameId,
38786             frameBorder : 'no',
38787             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
38788         }, this.el
38789         );
38790         
38791        // console.log(iframe);
38792         //this.wrap.dom.appendChild(iframe);
38793
38794         this.iframe = iframe.dom;
38795
38796          this.assignDocWin();
38797         
38798         this.doc.designMode = 'on';
38799        
38800         this.doc.open();
38801         this.doc.write(this.getDocMarkup());
38802         this.doc.close();
38803
38804         
38805         var task = { // must defer to wait for browser to be ready
38806             run : function(){
38807                 //console.log("run task?" + this.doc.readyState);
38808                 this.assignDocWin();
38809                 if(this.doc.body || this.doc.readyState == 'complete'){
38810                     try {
38811                         this.doc.designMode="on";
38812                     } catch (e) {
38813                         return;
38814                     }
38815                     Roo.TaskMgr.stop(task);
38816                     this.initEditor.defer(10, this);
38817                 }
38818             },
38819             interval : 10,
38820             duration:10000,
38821             scope: this
38822         };
38823         Roo.TaskMgr.start(task);
38824
38825         if(!this.width){
38826             this.setSize(this.wrap.getSize());
38827         }
38828         if (this.resizeEl) {
38829             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
38830             // should trigger onReize..
38831         }
38832     },
38833
38834     // private
38835     onResize : function(w, h)
38836     {
38837         //Roo.log('resize: ' +w + ',' + h );
38838         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
38839         if(this.el && this.iframe){
38840             if(typeof w == 'number'){
38841                 var aw = w - this.wrap.getFrameWidth('lr');
38842                 this.el.setWidth(this.adjustWidth('textarea', aw));
38843                 this.iframe.style.width = aw + 'px';
38844             }
38845             if(typeof h == 'number'){
38846                 var tbh = 0;
38847                 for (var i =0; i < this.toolbars.length;i++) {
38848                     // fixme - ask toolbars for heights?
38849                     tbh += this.toolbars[i].tb.el.getHeight();
38850                     if (this.toolbars[i].footer) {
38851                         tbh += this.toolbars[i].footer.el.getHeight();
38852                     }
38853                 }
38854                 
38855                 
38856                 
38857                 
38858                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
38859                 ah -= 5; // knock a few pixes off for look..
38860                 this.el.setHeight(this.adjustWidth('textarea', ah));
38861                 this.iframe.style.height = ah + 'px';
38862                 if(this.doc){
38863                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
38864                 }
38865             }
38866         }
38867     },
38868
38869     /**
38870      * Toggles the editor between standard and source edit mode.
38871      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
38872      */
38873     toggleSourceEdit : function(sourceEditMode){
38874         
38875         this.sourceEditMode = sourceEditMode === true;
38876         
38877         if(this.sourceEditMode){
38878           
38879             this.syncValue();
38880             this.iframe.className = 'x-hidden';
38881             this.el.removeClass('x-hidden');
38882             this.el.dom.removeAttribute('tabIndex');
38883             this.el.focus();
38884         }else{
38885              
38886             this.pushValue();
38887             this.iframe.className = '';
38888             this.el.addClass('x-hidden');
38889             this.el.dom.setAttribute('tabIndex', -1);
38890             this.deferFocus();
38891         }
38892         this.setSize(this.wrap.getSize());
38893         this.fireEvent('editmodechange', this, this.sourceEditMode);
38894     },
38895
38896     // private used internally
38897     createLink : function(){
38898         var url = prompt(this.createLinkText, this.defaultLinkValue);
38899         if(url && url != 'http:/'+'/'){
38900             this.relayCmd('createlink', url);
38901         }
38902     },
38903
38904     // private (for BoxComponent)
38905     adjustSize : Roo.BoxComponent.prototype.adjustSize,
38906
38907     // private (for BoxComponent)
38908     getResizeEl : function(){
38909         return this.wrap;
38910     },
38911
38912     // private (for BoxComponent)
38913     getPositionEl : function(){
38914         return this.wrap;
38915     },
38916
38917     // private
38918     initEvents : function(){
38919         this.originalValue = this.getValue();
38920     },
38921
38922     /**
38923      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
38924      * @method
38925      */
38926     markInvalid : Roo.emptyFn,
38927     /**
38928      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
38929      * @method
38930      */
38931     clearInvalid : Roo.emptyFn,
38932
38933     setValue : function(v){
38934         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
38935         this.pushValue();
38936     },
38937
38938     /**
38939      * Protected method that will not generally be called directly. If you need/want
38940      * custom HTML cleanup, this is the method you should override.
38941      * @param {String} html The HTML to be cleaned
38942      * return {String} The cleaned HTML
38943      */
38944     cleanHtml : function(html){
38945         html = String(html);
38946         if(html.length > 5){
38947             if(Roo.isSafari){ // strip safari nonsense
38948                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
38949             }
38950         }
38951         if(html == '&nbsp;'){
38952             html = '';
38953         }
38954         return html;
38955     },
38956
38957     /**
38958      * Protected method that will not generally be called directly. Syncs the contents
38959      * of the editor iframe with the textarea.
38960      */
38961     syncValue : function(){
38962         if(this.initialized){
38963             var bd = (this.doc.body || this.doc.documentElement);
38964             this.cleanUpPaste();
38965             var html = bd.innerHTML;
38966             if(Roo.isSafari){
38967                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
38968                 var m = bs.match(/text-align:(.*?);/i);
38969                 if(m && m[1]){
38970                     html = '<div style="'+m[0]+'">' + html + '</div>';
38971                 }
38972             }
38973             html = this.cleanHtml(html);
38974             if(this.fireEvent('beforesync', this, html) !== false){
38975                 this.el.dom.value = html;
38976                 this.fireEvent('sync', this, html);
38977             }
38978         }
38979     },
38980
38981     /**
38982      * Protected method that will not generally be called directly. Pushes the value of the textarea
38983      * into the iframe editor.
38984      */
38985     pushValue : function(){
38986         if(this.initialized){
38987             var v = this.el.dom.value;
38988             if(v.length < 1){
38989                 v = '&#160;';
38990             }
38991             
38992             if(this.fireEvent('beforepush', this, v) !== false){
38993                 var d = (this.doc.body || this.doc.documentElement);
38994                 d.innerHTML = v;
38995                 this.cleanUpPaste();
38996                 this.el.dom.value = d.innerHTML;
38997                 this.fireEvent('push', this, v);
38998             }
38999         }
39000     },
39001
39002     // private
39003     deferFocus : function(){
39004         this.focus.defer(10, this);
39005     },
39006
39007     // doc'ed in Field
39008     focus : function(){
39009         if(this.win && !this.sourceEditMode){
39010             this.win.focus();
39011         }else{
39012             this.el.focus();
39013         }
39014     },
39015     
39016     assignDocWin: function()
39017     {
39018         var iframe = this.iframe;
39019         
39020          if(Roo.isIE){
39021             this.doc = iframe.contentWindow.document;
39022             this.win = iframe.contentWindow;
39023         } else {
39024             if (!Roo.get(this.frameId)) {
39025                 return;
39026             }
39027             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
39028             this.win = Roo.get(this.frameId).dom.contentWindow;
39029         }
39030     },
39031     
39032     // private
39033     initEditor : function(){
39034         //console.log("INIT EDITOR");
39035         this.assignDocWin();
39036         
39037         
39038         
39039         this.doc.designMode="on";
39040         this.doc.open();
39041         this.doc.write(this.getDocMarkup());
39042         this.doc.close();
39043         
39044         var dbody = (this.doc.body || this.doc.documentElement);
39045         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
39046         // this copies styles from the containing element into thsi one..
39047         // not sure why we need all of this..
39048         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
39049         ss['background-attachment'] = 'fixed'; // w3c
39050         dbody.bgProperties = 'fixed'; // ie
39051         Roo.DomHelper.applyStyles(dbody, ss);
39052         Roo.EventManager.on(this.doc, {
39053             //'mousedown': this.onEditorEvent,
39054             'mouseup': this.onEditorEvent,
39055             'dblclick': this.onEditorEvent,
39056             'click': this.onEditorEvent,
39057             'keyup': this.onEditorEvent,
39058             buffer:100,
39059             scope: this
39060         });
39061         if(Roo.isGecko){
39062             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
39063         }
39064         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
39065             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
39066         }
39067         this.initialized = true;
39068
39069         this.fireEvent('initialize', this);
39070         this.pushValue();
39071     },
39072
39073     // private
39074     onDestroy : function(){
39075         
39076         
39077         
39078         if(this.rendered){
39079             
39080             for (var i =0; i < this.toolbars.length;i++) {
39081                 // fixme - ask toolbars for heights?
39082                 this.toolbars[i].onDestroy();
39083             }
39084             
39085             this.wrap.dom.innerHTML = '';
39086             this.wrap.remove();
39087         }
39088     },
39089
39090     // private
39091     onFirstFocus : function(){
39092         
39093         this.assignDocWin();
39094         
39095         
39096         this.activated = true;
39097         for (var i =0; i < this.toolbars.length;i++) {
39098             this.toolbars[i].onFirstFocus();
39099         }
39100        
39101         if(Roo.isGecko){ // prevent silly gecko errors
39102             this.win.focus();
39103             var s = this.win.getSelection();
39104             if(!s.focusNode || s.focusNode.nodeType != 3){
39105                 var r = s.getRangeAt(0);
39106                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
39107                 r.collapse(true);
39108                 this.deferFocus();
39109             }
39110             try{
39111                 this.execCmd('useCSS', true);
39112                 this.execCmd('styleWithCSS', false);
39113             }catch(e){}
39114         }
39115         this.fireEvent('activate', this);
39116     },
39117
39118     // private
39119     adjustFont: function(btn){
39120         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
39121         //if(Roo.isSafari){ // safari
39122         //    adjust *= 2;
39123        // }
39124         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
39125         if(Roo.isSafari){ // safari
39126             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
39127             v =  (v < 10) ? 10 : v;
39128             v =  (v > 48) ? 48 : v;
39129             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
39130             
39131         }
39132         
39133         
39134         v = Math.max(1, v+adjust);
39135         
39136         this.execCmd('FontSize', v  );
39137     },
39138
39139     onEditorEvent : function(e){
39140         this.fireEvent('editorevent', this, e);
39141       //  this.updateToolbar();
39142         this.syncValue();
39143     },
39144
39145     insertTag : function(tg)
39146     {
39147         // could be a bit smarter... -> wrap the current selected tRoo..
39148         
39149         this.execCmd("formatblock",   tg);
39150         
39151     },
39152     
39153     insertText : function(txt)
39154     {
39155         
39156         
39157         range = this.createRange();
39158         range.deleteContents();
39159                //alert(Sender.getAttribute('label'));
39160                
39161         range.insertNode(this.doc.createTextNode(txt));
39162     } ,
39163     
39164     // private
39165     relayBtnCmd : function(btn){
39166         this.relayCmd(btn.cmd);
39167     },
39168
39169     /**
39170      * Executes a Midas editor command on the editor document and performs necessary focus and
39171      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
39172      * @param {String} cmd The Midas command
39173      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
39174      */
39175     relayCmd : function(cmd, value){
39176         this.win.focus();
39177         this.execCmd(cmd, value);
39178         this.fireEvent('editorevent', this);
39179         //this.updateToolbar();
39180         this.deferFocus();
39181     },
39182
39183     /**
39184      * Executes a Midas editor command directly on the editor document.
39185      * For visual commands, you should use {@link #relayCmd} instead.
39186      * <b>This should only be called after the editor is initialized.</b>
39187      * @param {String} cmd The Midas command
39188      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
39189      */
39190     execCmd : function(cmd, value){
39191         this.doc.execCommand(cmd, false, value === undefined ? null : value);
39192         this.syncValue();
39193     },
39194
39195    
39196     /**
39197      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
39198      * to insert tRoo.
39199      * @param {String} text
39200      */
39201     insertAtCursor : function(text){
39202         if(!this.activated){
39203             return;
39204         }
39205         if(Roo.isIE){
39206             this.win.focus();
39207             var r = this.doc.selection.createRange();
39208             if(r){
39209                 r.collapse(true);
39210                 r.pasteHTML(text);
39211                 this.syncValue();
39212                 this.deferFocus();
39213             }
39214         }else if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
39215             this.win.focus();
39216             this.execCmd('InsertHTML', text);
39217             this.deferFocus();
39218         }
39219     },
39220  // private
39221     mozKeyPress : function(e){
39222         if(e.ctrlKey){
39223             var c = e.getCharCode(), cmd;
39224           
39225             if(c > 0){
39226                 c = String.fromCharCode(c).toLowerCase();
39227                 switch(c){
39228                     case 'b':
39229                         cmd = 'bold';
39230                     break;
39231                     case 'i':
39232                         cmd = 'italic';
39233                     break;
39234                     case 'u':
39235                         cmd = 'underline';
39236                     case 'v':
39237                         this.cleanUpPaste.defer(100, this);
39238                         return;
39239                     break;
39240                 }
39241                 if(cmd){
39242                     this.win.focus();
39243                     this.execCmd(cmd);
39244                     this.deferFocus();
39245                     e.preventDefault();
39246                 }
39247                 
39248             }
39249         }
39250     },
39251
39252     // private
39253     fixKeys : function(){ // load time branching for fastest keydown performance
39254         if(Roo.isIE){
39255             return function(e){
39256                 var k = e.getKey(), r;
39257                 if(k == e.TAB){
39258                     e.stopEvent();
39259                     r = this.doc.selection.createRange();
39260                     if(r){
39261                         r.collapse(true);
39262                         r.pasteHTML('&#160;&#160;&#160;&#160;');
39263                         this.deferFocus();
39264                     }
39265                     return;
39266                 }
39267                 
39268                 if(k == e.ENTER){
39269                     r = this.doc.selection.createRange();
39270                     if(r){
39271                         var target = r.parentElement();
39272                         if(!target || target.tagName.toLowerCase() != 'li'){
39273                             e.stopEvent();
39274                             r.pasteHTML('<br />');
39275                             r.collapse(false);
39276                             r.select();
39277                         }
39278                     }
39279                 }
39280                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
39281                     this.cleanUpPaste.defer(100, this);
39282                     return;
39283                 }
39284                 
39285                 
39286             };
39287         }else if(Roo.isOpera){
39288             return function(e){
39289                 var k = e.getKey();
39290                 if(k == e.TAB){
39291                     e.stopEvent();
39292                     this.win.focus();
39293                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
39294                     this.deferFocus();
39295                 }
39296                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
39297                     this.cleanUpPaste.defer(100, this);
39298                     return;
39299                 }
39300                 
39301             };
39302         }else if(Roo.isSafari){
39303             return function(e){
39304                 var k = e.getKey();
39305                 
39306                 if(k == e.TAB){
39307                     e.stopEvent();
39308                     this.execCmd('InsertText','\t');
39309                     this.deferFocus();
39310                     return;
39311                 }
39312                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
39313                     this.cleanUpPaste.defer(100, this);
39314                     return;
39315                 }
39316                 
39317              };
39318         }
39319     }(),
39320     
39321     getAllAncestors: function()
39322     {
39323         var p = this.getSelectedNode();
39324         var a = [];
39325         if (!p) {
39326             a.push(p); // push blank onto stack..
39327             p = this.getParentElement();
39328         }
39329         
39330         
39331         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
39332             a.push(p);
39333             p = p.parentNode;
39334         }
39335         a.push(this.doc.body);
39336         return a;
39337     },
39338     lastSel : false,
39339     lastSelNode : false,
39340     
39341     
39342     getSelection : function() 
39343     {
39344         this.assignDocWin();
39345         return Roo.isIE ? this.doc.selection : this.win.getSelection();
39346     },
39347     
39348     getSelectedNode: function() 
39349     {
39350         // this may only work on Gecko!!!
39351         
39352         // should we cache this!!!!
39353         
39354         
39355         
39356          
39357         var range = this.createRange(this.getSelection()).cloneRange();
39358         
39359         if (Roo.isIE) {
39360             var parent = range.parentElement();
39361             while (true) {
39362                 var testRange = range.duplicate();
39363                 testRange.moveToElementText(parent);
39364                 if (testRange.inRange(range)) {
39365                     break;
39366                 }
39367                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
39368                     break;
39369                 }
39370                 parent = parent.parentElement;
39371             }
39372             return parent;
39373         }
39374         
39375         // is ancestor a text element.
39376         var ac =  range.commonAncestorContainer;
39377         if (ac.nodeType == 3) {
39378             ac = ac.parentNode;
39379         }
39380         
39381         var ar = ac.childNodes;
39382          
39383         var nodes = [];
39384         var other_nodes = [];
39385         var has_other_nodes = false;
39386         for (var i=0;i<ar.length;i++) {
39387             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
39388                 continue;
39389             }
39390             // fullly contained node.
39391             
39392             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
39393                 nodes.push(ar[i]);
39394                 continue;
39395             }
39396             
39397             // probably selected..
39398             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
39399                 other_nodes.push(ar[i]);
39400                 continue;
39401             }
39402             // outer..
39403             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
39404                 continue;
39405             }
39406             
39407             
39408             has_other_nodes = true;
39409         }
39410         if (!nodes.length && other_nodes.length) {
39411             nodes= other_nodes;
39412         }
39413         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
39414             return false;
39415         }
39416         
39417         return nodes[0];
39418     },
39419     createRange: function(sel)
39420     {
39421         // this has strange effects when using with 
39422         // top toolbar - not sure if it's a great idea.
39423         //this.editor.contentWindow.focus();
39424         if (typeof sel != "undefined") {
39425             try {
39426                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
39427             } catch(e) {
39428                 return this.doc.createRange();
39429             }
39430         } else {
39431             return this.doc.createRange();
39432         }
39433     },
39434     getParentElement: function()
39435     {
39436         
39437         this.assignDocWin();
39438         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
39439         
39440         var range = this.createRange(sel);
39441          
39442         try {
39443             var p = range.commonAncestorContainer;
39444             while (p.nodeType == 3) { // text node
39445                 p = p.parentNode;
39446             }
39447             return p;
39448         } catch (e) {
39449             return null;
39450         }
39451     
39452     },
39453     /***
39454      *
39455      * Range intersection.. the hard stuff...
39456      *  '-1' = before
39457      *  '0' = hits..
39458      *  '1' = after.
39459      *         [ -- selected range --- ]
39460      *   [fail]                        [fail]
39461      *
39462      *    basically..
39463      *      if end is before start or  hits it. fail.
39464      *      if start is after end or hits it fail.
39465      *
39466      *   if either hits (but other is outside. - then it's not 
39467      *   
39468      *    
39469      **/
39470     
39471     
39472     // @see http://www.thismuchiknow.co.uk/?p=64.
39473     rangeIntersectsNode : function(range, node)
39474     {
39475         var nodeRange = node.ownerDocument.createRange();
39476         try {
39477             nodeRange.selectNode(node);
39478         } catch (e) {
39479             nodeRange.selectNodeContents(node);
39480         }
39481     
39482         var rangeStartRange = range.cloneRange();
39483         rangeStartRange.collapse(true);
39484     
39485         var rangeEndRange = range.cloneRange();
39486         rangeEndRange.collapse(false);
39487     
39488         var nodeStartRange = nodeRange.cloneRange();
39489         nodeStartRange.collapse(true);
39490     
39491         var nodeEndRange = nodeRange.cloneRange();
39492         nodeEndRange.collapse(false);
39493     
39494         return rangeStartRange.compareBoundaryPoints(
39495                  Range.START_TO_START, nodeEndRange) == -1 &&
39496                rangeEndRange.compareBoundaryPoints(
39497                  Range.START_TO_START, nodeStartRange) == 1;
39498         
39499          
39500     },
39501     rangeCompareNode : function(range, node)
39502     {
39503         var nodeRange = node.ownerDocument.createRange();
39504         try {
39505             nodeRange.selectNode(node);
39506         } catch (e) {
39507             nodeRange.selectNodeContents(node);
39508         }
39509         
39510         
39511         range.collapse(true);
39512     
39513         nodeRange.collapse(true);
39514      
39515         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
39516         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
39517          
39518         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
39519         
39520         var nodeIsBefore   =  ss == 1;
39521         var nodeIsAfter    = ee == -1;
39522         
39523         if (nodeIsBefore && nodeIsAfter)
39524             return 0; // outer
39525         if (!nodeIsBefore && nodeIsAfter)
39526             return 1; //right trailed.
39527         
39528         if (nodeIsBefore && !nodeIsAfter)
39529             return 2;  // left trailed.
39530         // fully contined.
39531         return 3;
39532     },
39533
39534     // private? - in a new class?
39535     cleanUpPaste :  function()
39536     {
39537         // cleans up the whole document..
39538       //  console.log('cleanuppaste');
39539         this.cleanUpChildren(this.doc.body);
39540         
39541         
39542     },
39543     cleanUpChildren : function (n)
39544     {
39545         if (!n.childNodes.length) {
39546             return;
39547         }
39548         for (var i = n.childNodes.length-1; i > -1 ; i--) {
39549            this.cleanUpChild(n.childNodes[i]);
39550         }
39551     },
39552     
39553     
39554         
39555     
39556     cleanUpChild : function (node)
39557     {
39558         //console.log(node);
39559         if (node.nodeName == "#text") {
39560             // clean up silly Windows -- stuff?
39561             return; 
39562         }
39563         if (node.nodeName == "#comment") {
39564             node.parentNode.removeChild(node);
39565             // clean up silly Windows -- stuff?
39566             return; 
39567         }
39568         
39569         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
39570             // remove node.
39571             node.parentNode.removeChild(node);
39572             return;
39573             
39574         }
39575         if (Roo.form.HtmlEditor.remove.indexOf(node.tagName.toLowerCase()) > -1) {
39576             this.cleanUpChildren(node);
39577             // inserts everything just before this node...
39578             while (node.childNodes.length) {
39579                 var cn = node.childNodes[0];
39580                 node.removeChild(cn);
39581                 node.parentNode.insertBefore(cn, node);
39582             }
39583             node.parentNode.removeChild(node);
39584             return;
39585         }
39586         
39587         if (!node.attributes || !node.attributes.length) {
39588             this.cleanUpChildren(node);
39589             return;
39590         }
39591         
39592         function cleanAttr(n,v)
39593         {
39594             
39595             if (v.match(/^\./) || v.match(/^\//)) {
39596                 return;
39597             }
39598             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
39599                 return;
39600             }
39601             Roo.log("(REMOVE)"+ node.tagName +'.' + n + '=' + v);
39602             node.removeAttribute(n);
39603             
39604         }
39605         
39606         function cleanStyle(n,v)
39607         {
39608             if (v.match(/expression/)) { //XSS?? should we even bother..
39609                 node.removeAttribute(n);
39610                 return;
39611             }
39612             
39613             
39614             var parts = v.split(/;/);
39615             Roo.each(parts, function(p) {
39616                 p = p.replace(/\s+/g,'');
39617                 if (!p.length) {
39618                     return true;
39619                 }
39620                 var l = p.split(':').shift().replace(/\s+/g,'');
39621                 
39622                 // only allow 'c whitelisted system attributes'
39623                 if (Roo.form.HtmlEditor.cwhite.indexOf(l) < 0) {
39624                     Roo.log('(REMOVE)' + node.tagName +'.' + n + ':'+l + '=' + v);
39625                     node.removeAttribute(n);
39626                     return false;
39627                 }
39628                 return true;
39629             });
39630             
39631             
39632         }
39633         
39634         
39635         for (var i = node.attributes.length-1; i > -1 ; i--) {
39636             var a = node.attributes[i];
39637             //console.log(a);
39638             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
39639                 node.removeAttribute(a.name);
39640                 return;
39641             }
39642             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
39643                 cleanAttr(a.name,a.value); // fixme..
39644                 return;
39645             }
39646             if (a.name == 'style') {
39647                 cleanStyle(a.name,a.value);
39648             }
39649             /// clean up MS crap..
39650             if (a.name == 'class') {
39651                 if (a.value.match(/^Mso/)) {
39652                     node.className = '';
39653                 }
39654             }
39655             
39656             // style cleanup!?
39657             // class cleanup?
39658             
39659         }
39660         
39661         
39662         this.cleanUpChildren(node);
39663         
39664         
39665     }
39666     
39667     
39668     // hide stuff that is not compatible
39669     /**
39670      * @event blur
39671      * @hide
39672      */
39673     /**
39674      * @event change
39675      * @hide
39676      */
39677     /**
39678      * @event focus
39679      * @hide
39680      */
39681     /**
39682      * @event specialkey
39683      * @hide
39684      */
39685     /**
39686      * @cfg {String} fieldClass @hide
39687      */
39688     /**
39689      * @cfg {String} focusClass @hide
39690      */
39691     /**
39692      * @cfg {String} autoCreate @hide
39693      */
39694     /**
39695      * @cfg {String} inputType @hide
39696      */
39697     /**
39698      * @cfg {String} invalidClass @hide
39699      */
39700     /**
39701      * @cfg {String} invalidText @hide
39702      */
39703     /**
39704      * @cfg {String} msgFx @hide
39705      */
39706     /**
39707      * @cfg {String} validateOnBlur @hide
39708      */
39709 });
39710
39711 Roo.form.HtmlEditor.white = [
39712         'area', 'br', 'img', 'input', 'hr', 'wbr',
39713         
39714        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
39715        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
39716        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
39717        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
39718        'table',   'ul',         'xmp', 
39719        
39720        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
39721       'thead',   'tr', 
39722      
39723       'dir', 'menu', 'ol', 'ul', 'dl',
39724        
39725       'embed',  'object'
39726 ];
39727
39728
39729 Roo.form.HtmlEditor.black = [
39730     //    'embed',  'object', // enable - backend responsiblity to clean thiese
39731         'applet', // 
39732         'base',   'basefont', 'bgsound', 'blink',  'body', 
39733         'frame',  'frameset', 'head',    'html',   'ilayer', 
39734         'iframe', 'layer',  'link',     'meta',    'object',   
39735         'script', 'style' ,'title',  'xml' // clean later..
39736 ];
39737 Roo.form.HtmlEditor.clean = [
39738     'script', 'style', 'title', 'xml'
39739 ];
39740 Roo.form.HtmlEditor.remove = [
39741     'font'
39742 ];
39743 // attributes..
39744
39745 Roo.form.HtmlEditor.ablack = [
39746     'on'
39747 ];
39748     
39749 Roo.form.HtmlEditor.aclean = [ 
39750     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
39751 ];
39752
39753 // protocols..
39754 Roo.form.HtmlEditor.pwhite= [
39755         'http',  'https',  'mailto'
39756 ];
39757
39758 // white listed style attributes.
39759 Roo.form.HtmlEditor.cwhite= [
39760         'text-align',
39761         'font-size'
39762 ];
39763
39764 // <script type="text/javascript">
39765 /*
39766  * Based on
39767  * Ext JS Library 1.1.1
39768  * Copyright(c) 2006-2007, Ext JS, LLC.
39769  *  
39770  
39771  */
39772
39773 /**
39774  * @class Roo.form.HtmlEditorToolbar1
39775  * Basic Toolbar
39776  * 
39777  * Usage:
39778  *
39779  new Roo.form.HtmlEditor({
39780     ....
39781     toolbars : [
39782         new Roo.form.HtmlEditorToolbar1({
39783             disable : { fonts: 1 , format: 1, ..., ... , ...],
39784             btns : [ .... ]
39785         })
39786     }
39787      
39788  * 
39789  * @cfg {Object} disable List of elements to disable..
39790  * @cfg {Array} btns List of additional buttons.
39791  * 
39792  * 
39793  * NEEDS Extra CSS? 
39794  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
39795  */
39796  
39797 Roo.form.HtmlEditor.ToolbarStandard = function(config)
39798 {
39799     
39800     Roo.apply(this, config);
39801     
39802     // default disabled, based on 'good practice'..
39803     this.disable = this.disable || {};
39804     Roo.applyIf(this.disable, {
39805         fontSize : true,
39806         colors : true,
39807         specialElements : true
39808     });
39809     
39810     
39811     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
39812     // dont call parent... till later.
39813 }
39814
39815 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
39816     
39817     tb: false,
39818     
39819     rendered: false,
39820     
39821     editor : false,
39822     /**
39823      * @cfg {Object} disable  List of toolbar elements to disable
39824          
39825      */
39826     disable : false,
39827       /**
39828      * @cfg {Array} fontFamilies An array of available font families
39829      */
39830     fontFamilies : [
39831         'Arial',
39832         'Courier New',
39833         'Tahoma',
39834         'Times New Roman',
39835         'Verdana'
39836     ],
39837     
39838     specialChars : [
39839            "&#169;",
39840           "&#174;",     
39841           "&#8482;",    
39842           "&#163;" ,    
39843          // "&#8212;",    
39844           "&#8230;",    
39845           "&#247;" ,    
39846         //  "&#225;" ,     ?? a acute?
39847            "&#8364;"    , //Euro
39848        //   "&#8220;"    ,
39849         //  "&#8221;"    ,
39850         //  "&#8226;"    ,
39851           "&#176;"  //   , // degrees
39852
39853          // "&#233;"     , // e ecute
39854          // "&#250;"     , // u ecute?
39855     ],
39856     
39857     specialElements : [
39858         {
39859             text: "Insert Table",
39860             xtype: 'MenuItem',
39861             xns : Roo.Menu,
39862             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
39863                 
39864         },
39865         {    
39866             text: "Insert Image",
39867             xtype: 'MenuItem',
39868             xns : Roo.Menu,
39869             ihtml : '<img src="about:blank"/>'
39870             
39871         }
39872         
39873          
39874     ],
39875     
39876     
39877     inputElements : [ 
39878             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
39879             "input:submit", "input:button", "select", "textarea", "label" ],
39880     formats : [
39881         ["p"] ,  
39882         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
39883         ["pre"],[ "code"], 
39884         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"]
39885     ],
39886      /**
39887      * @cfg {String} defaultFont default font to use.
39888      */
39889     defaultFont: 'tahoma',
39890    
39891     fontSelect : false,
39892     
39893     
39894     formatCombo : false,
39895     
39896     init : function(editor)
39897     {
39898         this.editor = editor;
39899         
39900         
39901         var fid = editor.frameId;
39902         var etb = this;
39903         function btn(id, toggle, handler){
39904             var xid = fid + '-'+ id ;
39905             return {
39906                 id : xid,
39907                 cmd : id,
39908                 cls : 'x-btn-icon x-edit-'+id,
39909                 enableToggle:toggle !== false,
39910                 scope: editor, // was editor...
39911                 handler:handler||editor.relayBtnCmd,
39912                 clickEvent:'mousedown',
39913                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
39914                 tabIndex:-1
39915             };
39916         }
39917         
39918         
39919         
39920         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
39921         this.tb = tb;
39922          // stop form submits
39923         tb.el.on('click', function(e){
39924             e.preventDefault(); // what does this do?
39925         });
39926
39927         if(!this.disable.font && !Roo.isSafari){
39928             /* why no safari for fonts
39929             editor.fontSelect = tb.el.createChild({
39930                 tag:'select',
39931                 tabIndex: -1,
39932                 cls:'x-font-select',
39933                 html: editor.createFontOptions()
39934             });
39935             editor.fontSelect.on('change', function(){
39936                 var font = editor.fontSelect.dom.value;
39937                 editor.relayCmd('fontname', font);
39938                 editor.deferFocus();
39939             }, editor);
39940             tb.add(
39941                 editor.fontSelect.dom,
39942                 '-'
39943             );
39944             */
39945         };
39946         if(!this.disable.formats){
39947             this.formatCombo = new Roo.form.ComboBox({
39948                 store: new Roo.data.SimpleStore({
39949                     id : 'tag',
39950                     fields: ['tag'],
39951                     data : this.formats // from states.js
39952                 }),
39953                 blockFocus : true,
39954                 //autoCreate : {tag: "div",  size: "20"},
39955                 displayField:'tag',
39956                 typeAhead: false,
39957                 mode: 'local',
39958                 editable : false,
39959                 triggerAction: 'all',
39960                 emptyText:'Add tag',
39961                 selectOnFocus:true,
39962                 width:135,
39963                 listeners : {
39964                     'select': function(c, r, i) {
39965                         editor.insertTag(r.get('tag'));
39966                         editor.focus();
39967                     }
39968                 }
39969
39970             });
39971             tb.addField(this.formatCombo);
39972             
39973         }
39974         
39975         if(!this.disable.format){
39976             tb.add(
39977                 btn('bold'),
39978                 btn('italic'),
39979                 btn('underline')
39980             );
39981         };
39982         if(!this.disable.fontSize){
39983             tb.add(
39984                 '-',
39985                 
39986                 
39987                 btn('increasefontsize', false, editor.adjustFont),
39988                 btn('decreasefontsize', false, editor.adjustFont)
39989             );
39990         };
39991         
39992         
39993         if(!this.disable.colors){
39994             tb.add(
39995                 '-', {
39996                     id:editor.frameId +'-forecolor',
39997                     cls:'x-btn-icon x-edit-forecolor',
39998                     clickEvent:'mousedown',
39999                     tooltip: this.buttonTips['forecolor'] || undefined,
40000                     tabIndex:-1,
40001                     menu : new Roo.menu.ColorMenu({
40002                         allowReselect: true,
40003                         focus: Roo.emptyFn,
40004                         value:'000000',
40005                         plain:true,
40006                         selectHandler: function(cp, color){
40007                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
40008                             editor.deferFocus();
40009                         },
40010                         scope: editor,
40011                         clickEvent:'mousedown'
40012                     })
40013                 }, {
40014                     id:editor.frameId +'backcolor',
40015                     cls:'x-btn-icon x-edit-backcolor',
40016                     clickEvent:'mousedown',
40017                     tooltip: this.buttonTips['backcolor'] || undefined,
40018                     tabIndex:-1,
40019                     menu : new Roo.menu.ColorMenu({
40020                         focus: Roo.emptyFn,
40021                         value:'FFFFFF',
40022                         plain:true,
40023                         allowReselect: true,
40024                         selectHandler: function(cp, color){
40025                             if(Roo.isGecko){
40026                                 editor.execCmd('useCSS', false);
40027                                 editor.execCmd('hilitecolor', color);
40028                                 editor.execCmd('useCSS', true);
40029                                 editor.deferFocus();
40030                             }else{
40031                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
40032                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
40033                                 editor.deferFocus();
40034                             }
40035                         },
40036                         scope:editor,
40037                         clickEvent:'mousedown'
40038                     })
40039                 }
40040             );
40041         };
40042         // now add all the items...
40043         
40044
40045         if(!this.disable.alignments){
40046             tb.add(
40047                 '-',
40048                 btn('justifyleft'),
40049                 btn('justifycenter'),
40050                 btn('justifyright')
40051             );
40052         };
40053
40054         //if(!Roo.isSafari){
40055             if(!this.disable.links){
40056                 tb.add(
40057                     '-',
40058                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
40059                 );
40060             };
40061
40062             if(!this.disable.lists){
40063                 tb.add(
40064                     '-',
40065                     btn('insertorderedlist'),
40066                     btn('insertunorderedlist')
40067                 );
40068             }
40069             if(!this.disable.sourceEdit){
40070                 tb.add(
40071                     '-',
40072                     btn('sourceedit', true, function(btn){
40073                         this.toggleSourceEdit(btn.pressed);
40074                     })
40075                 );
40076             }
40077         //}
40078         
40079         var smenu = { };
40080         // special menu.. - needs to be tidied up..
40081         if (!this.disable.special) {
40082             smenu = {
40083                 text: "&#169;",
40084                 cls: 'x-edit-none',
40085                 
40086                 menu : {
40087                     items : []
40088                 }
40089             };
40090             for (var i =0; i < this.specialChars.length; i++) {
40091                 smenu.menu.items.push({
40092                     
40093                     html: this.specialChars[i],
40094                     handler: function(a,b) {
40095                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
40096                         
40097                     },
40098                     tabIndex:-1
40099                 });
40100             }
40101             
40102             
40103             tb.add(smenu);
40104             
40105             
40106         }
40107          
40108         if (!this.disable.specialElements) {
40109             var semenu = {
40110                 text: "Other;",
40111                 cls: 'x-edit-none',
40112                 menu : {
40113                     items : []
40114                 }
40115             };
40116             for (var i =0; i < this.specialElements.length; i++) {
40117                 semenu.menu.items.push(
40118                     Roo.apply({ 
40119                         handler: function(a,b) {
40120                             editor.insertAtCursor(this.ihtml);
40121                         }
40122                     }, this.specialElements[i])
40123                 );
40124                     
40125             }
40126             
40127             tb.add(semenu);
40128             
40129             
40130         }
40131          
40132         
40133         if (this.btns) {
40134             for(var i =0; i< this.btns.length;i++) {
40135                 var b = this.btns[i];
40136                 b.cls =  'x-edit-none';
40137                 b.scope = editor;
40138                 tb.add(b);
40139             }
40140         
40141         }
40142         
40143         
40144         
40145         // disable everything...
40146         
40147         this.tb.items.each(function(item){
40148            if(item.id != editor.frameId+ '-sourceedit'){
40149                 item.disable();
40150             }
40151         });
40152         this.rendered = true;
40153         
40154         // the all the btns;
40155         editor.on('editorevent', this.updateToolbar, this);
40156         // other toolbars need to implement this..
40157         //editor.on('editmodechange', this.updateToolbar, this);
40158     },
40159     
40160     
40161     
40162     /**
40163      * Protected method that will not generally be called directly. It triggers
40164      * a toolbar update by reading the markup state of the current selection in the editor.
40165      */
40166     updateToolbar: function(){
40167
40168         if(!this.editor.activated){
40169             this.editor.onFirstFocus();
40170             return;
40171         }
40172
40173         var btns = this.tb.items.map, 
40174             doc = this.editor.doc,
40175             frameId = this.editor.frameId;
40176
40177         if(!this.disable.font && !Roo.isSafari){
40178             /*
40179             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
40180             if(name != this.fontSelect.dom.value){
40181                 this.fontSelect.dom.value = name;
40182             }
40183             */
40184         }
40185         if(!this.disable.format){
40186             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
40187             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
40188             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
40189         }
40190         if(!this.disable.alignments){
40191             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
40192             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
40193             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
40194         }
40195         if(!Roo.isSafari && !this.disable.lists){
40196             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
40197             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
40198         }
40199         
40200         var ans = this.editor.getAllAncestors();
40201         if (this.formatCombo) {
40202             
40203             
40204             var store = this.formatCombo.store;
40205             this.formatCombo.setValue("");
40206             for (var i =0; i < ans.length;i++) {
40207                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
40208                     // select it..
40209                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
40210                     break;
40211                 }
40212             }
40213         }
40214         
40215         
40216         
40217         // hides menus... - so this cant be on a menu...
40218         Roo.menu.MenuMgr.hideAll();
40219
40220         //this.editorsyncValue();
40221     },
40222    
40223     
40224     createFontOptions : function(){
40225         var buf = [], fs = this.fontFamilies, ff, lc;
40226         for(var i = 0, len = fs.length; i< len; i++){
40227             ff = fs[i];
40228             lc = ff.toLowerCase();
40229             buf.push(
40230                 '<option value="',lc,'" style="font-family:',ff,';"',
40231                     (this.defaultFont == lc ? ' selected="true">' : '>'),
40232                     ff,
40233                 '</option>'
40234             );
40235         }
40236         return buf.join('');
40237     },
40238     
40239     toggleSourceEdit : function(sourceEditMode){
40240         if(sourceEditMode === undefined){
40241             sourceEditMode = !this.sourceEditMode;
40242         }
40243         this.sourceEditMode = sourceEditMode === true;
40244         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
40245         // just toggle the button?
40246         if(btn.pressed !== this.editor.sourceEditMode){
40247             btn.toggle(this.editor.sourceEditMode);
40248             return;
40249         }
40250         
40251         if(this.sourceEditMode){
40252             this.tb.items.each(function(item){
40253                 if(item.cmd != 'sourceedit'){
40254                     item.disable();
40255                 }
40256             });
40257           
40258         }else{
40259             if(this.initialized){
40260                 this.tb.items.each(function(item){
40261                     item.enable();
40262                 });
40263             }
40264             
40265         }
40266         // tell the editor that it's been pressed..
40267         this.editor.toggleSourceEdit(sourceEditMode);
40268        
40269     },
40270      /**
40271      * Object collection of toolbar tooltips for the buttons in the editor. The key
40272      * is the command id associated with that button and the value is a valid QuickTips object.
40273      * For example:
40274 <pre><code>
40275 {
40276     bold : {
40277         title: 'Bold (Ctrl+B)',
40278         text: 'Make the selected text bold.',
40279         cls: 'x-html-editor-tip'
40280     },
40281     italic : {
40282         title: 'Italic (Ctrl+I)',
40283         text: 'Make the selected text italic.',
40284         cls: 'x-html-editor-tip'
40285     },
40286     ...
40287 </code></pre>
40288     * @type Object
40289      */
40290     buttonTips : {
40291         bold : {
40292             title: 'Bold (Ctrl+B)',
40293             text: 'Make the selected text bold.',
40294             cls: 'x-html-editor-tip'
40295         },
40296         italic : {
40297             title: 'Italic (Ctrl+I)',
40298             text: 'Make the selected text italic.',
40299             cls: 'x-html-editor-tip'
40300         },
40301         underline : {
40302             title: 'Underline (Ctrl+U)',
40303             text: 'Underline the selected text.',
40304             cls: 'x-html-editor-tip'
40305         },
40306         increasefontsize : {
40307             title: 'Grow Text',
40308             text: 'Increase the font size.',
40309             cls: 'x-html-editor-tip'
40310         },
40311         decreasefontsize : {
40312             title: 'Shrink Text',
40313             text: 'Decrease the font size.',
40314             cls: 'x-html-editor-tip'
40315         },
40316         backcolor : {
40317             title: 'Text Highlight Color',
40318             text: 'Change the background color of the selected text.',
40319             cls: 'x-html-editor-tip'
40320         },
40321         forecolor : {
40322             title: 'Font Color',
40323             text: 'Change the color of the selected text.',
40324             cls: 'x-html-editor-tip'
40325         },
40326         justifyleft : {
40327             title: 'Align Text Left',
40328             text: 'Align text to the left.',
40329             cls: 'x-html-editor-tip'
40330         },
40331         justifycenter : {
40332             title: 'Center Text',
40333             text: 'Center text in the editor.',
40334             cls: 'x-html-editor-tip'
40335         },
40336         justifyright : {
40337             title: 'Align Text Right',
40338             text: 'Align text to the right.',
40339             cls: 'x-html-editor-tip'
40340         },
40341         insertunorderedlist : {
40342             title: 'Bullet List',
40343             text: 'Start a bulleted list.',
40344             cls: 'x-html-editor-tip'
40345         },
40346         insertorderedlist : {
40347             title: 'Numbered List',
40348             text: 'Start a numbered list.',
40349             cls: 'x-html-editor-tip'
40350         },
40351         createlink : {
40352             title: 'Hyperlink',
40353             text: 'Make the selected text a hyperlink.',
40354             cls: 'x-html-editor-tip'
40355         },
40356         sourceedit : {
40357             title: 'Source Edit',
40358             text: 'Switch to source editing mode.',
40359             cls: 'x-html-editor-tip'
40360         }
40361     },
40362     // private
40363     onDestroy : function(){
40364         if(this.rendered){
40365             
40366             this.tb.items.each(function(item){
40367                 if(item.menu){
40368                     item.menu.removeAll();
40369                     if(item.menu.el){
40370                         item.menu.el.destroy();
40371                     }
40372                 }
40373                 item.destroy();
40374             });
40375              
40376         }
40377     },
40378     onFirstFocus: function() {
40379         this.tb.items.each(function(item){
40380            item.enable();
40381         });
40382     }
40383 });
40384
40385
40386
40387
40388 // <script type="text/javascript">
40389 /*
40390  * Based on
40391  * Ext JS Library 1.1.1
40392  * Copyright(c) 2006-2007, Ext JS, LLC.
40393  *  
40394  
40395  */
40396
40397  
40398 /**
40399  * @class Roo.form.HtmlEditor.ToolbarContext
40400  * Context Toolbar
40401  * 
40402  * Usage:
40403  *
40404  new Roo.form.HtmlEditor({
40405     ....
40406     toolbars : [
40407         { xtype: 'ToolbarStandard', styles : {} }
40408         { xtype: 'ToolbarContext', disable : {} }
40409     ]
40410 })
40411
40412      
40413  * 
40414  * @config : {Object} disable List of elements to disable.. (not done yet.)
40415  * @config : {Object} styles  Map of styles available.
40416  * 
40417  */
40418
40419 Roo.form.HtmlEditor.ToolbarContext = function(config)
40420 {
40421     
40422     Roo.apply(this, config);
40423     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
40424     // dont call parent... till later.
40425     this.styles = this.styles || {};
40426 }
40427 Roo.form.HtmlEditor.ToolbarContext.types = {
40428     'IMG' : {
40429         width : {
40430             title: "Width",
40431             width: 40
40432         },
40433         height:  {
40434             title: "Height",
40435             width: 40
40436         },
40437         align: {
40438             title: "Align",
40439             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
40440             width : 80
40441             
40442         },
40443         border: {
40444             title: "Border",
40445             width: 40
40446         },
40447         alt: {
40448             title: "Alt",
40449             width: 120
40450         },
40451         src : {
40452             title: "Src",
40453             width: 220
40454         }
40455         
40456     },
40457     'A' : {
40458         name : {
40459             title: "Name",
40460             width: 50
40461         },
40462         href:  {
40463             title: "Href",
40464             width: 220
40465         } // border?
40466         
40467     },
40468     'TABLE' : {
40469         rows : {
40470             title: "Rows",
40471             width: 20
40472         },
40473         cols : {
40474             title: "Cols",
40475             width: 20
40476         },
40477         width : {
40478             title: "Width",
40479             width: 40
40480         },
40481         height : {
40482             title: "Height",
40483             width: 40
40484         },
40485         border : {
40486             title: "Border",
40487             width: 20
40488         }
40489     },
40490     'TD' : {
40491         width : {
40492             title: "Width",
40493             width: 40
40494         },
40495         height : {
40496             title: "Height",
40497             width: 40
40498         },   
40499         align: {
40500             title: "Align",
40501             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
40502             width: 80
40503         },
40504         valign: {
40505             title: "Valign",
40506             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
40507             width: 80
40508         },
40509         colspan: {
40510             title: "Colspan",
40511             width: 20
40512             
40513         }
40514     },
40515     'INPUT' : {
40516         name : {
40517             title: "name",
40518             width: 120
40519         },
40520         value : {
40521             title: "Value",
40522             width: 120
40523         },
40524         width : {
40525             title: "Width",
40526             width: 40
40527         }
40528     },
40529     'LABEL' : {
40530         'for' : {
40531             title: "For",
40532             width: 120
40533         }
40534     },
40535     'TEXTAREA' : {
40536           name : {
40537             title: "name",
40538             width: 120
40539         },
40540         rows : {
40541             title: "Rows",
40542             width: 20
40543         },
40544         cols : {
40545             title: "Cols",
40546             width: 20
40547         }
40548     },
40549     'SELECT' : {
40550         name : {
40551             title: "name",
40552             width: 120
40553         },
40554         selectoptions : {
40555             title: "Options",
40556             width: 200
40557         }
40558     },
40559     
40560     // should we really allow this??
40561     // should this just be 
40562     'BODY' : {
40563         title : {
40564             title: "title",
40565             width: 200,
40566             disabled : true
40567         }
40568     },
40569     '*' : {
40570         // empty..
40571     }
40572 };
40573
40574
40575
40576 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
40577     
40578     tb: false,
40579     
40580     rendered: false,
40581     
40582     editor : false,
40583     /**
40584      * @cfg {Object} disable  List of toolbar elements to disable
40585          
40586      */
40587     disable : false,
40588     /**
40589      * @cfg {Object} styles List of styles 
40590      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
40591      *
40592      * These must be defined in the page, so they get rendered correctly..
40593      * .headline { }
40594      * TD.underline { }
40595      * 
40596      */
40597     styles : false,
40598     
40599     
40600     
40601     toolbars : false,
40602     
40603     init : function(editor)
40604     {
40605         this.editor = editor;
40606         
40607         
40608         var fid = editor.frameId;
40609         var etb = this;
40610         function btn(id, toggle, handler){
40611             var xid = fid + '-'+ id ;
40612             return {
40613                 id : xid,
40614                 cmd : id,
40615                 cls : 'x-btn-icon x-edit-'+id,
40616                 enableToggle:toggle !== false,
40617                 scope: editor, // was editor...
40618                 handler:handler||editor.relayBtnCmd,
40619                 clickEvent:'mousedown',
40620                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
40621                 tabIndex:-1
40622             };
40623         }
40624         // create a new element.
40625         var wdiv = editor.wrap.createChild({
40626                 tag: 'div'
40627             }, editor.wrap.dom.firstChild.nextSibling, true);
40628         
40629         // can we do this more than once??
40630         
40631          // stop form submits
40632       
40633  
40634         // disable everything...
40635         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
40636         this.toolbars = {};
40637            
40638         for (var i in  ty) {
40639           
40640             this.toolbars[i] = this.buildToolbar(ty[i],i);
40641         }
40642         this.tb = this.toolbars.BODY;
40643         this.tb.el.show();
40644         this.buildFooter();
40645         this.footer.show();
40646          
40647         this.rendered = true;
40648         
40649         // the all the btns;
40650         editor.on('editorevent', this.updateToolbar, this);
40651         // other toolbars need to implement this..
40652         //editor.on('editmodechange', this.updateToolbar, this);
40653     },
40654     
40655     
40656     
40657     /**
40658      * Protected method that will not generally be called directly. It triggers
40659      * a toolbar update by reading the markup state of the current selection in the editor.
40660      */
40661     updateToolbar: function(ignore_a,ignore_b,sel){
40662
40663         
40664         if(!this.editor.activated){
40665              this.editor.onFirstFocus();
40666             return;
40667         }
40668         var updateFooter = sel ? false : true;
40669         
40670         
40671         var ans = this.editor.getAllAncestors();
40672         
40673         // pick
40674         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
40675         
40676         if (!sel) { 
40677             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
40678             sel = sel ? sel : this.editor.doc.body;
40679             sel = sel.tagName.length ? sel : this.editor.doc.body;
40680             
40681         }
40682         // pick a menu that exists..
40683         var tn = sel.tagName.toUpperCase();
40684         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
40685         
40686         tn = sel.tagName.toUpperCase();
40687         
40688         var lastSel = this.tb.selectedNode
40689         
40690         this.tb.selectedNode = sel;
40691         
40692         // if current menu does not match..
40693         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
40694                 
40695             this.tb.el.hide();
40696             ///console.log("show: " + tn);
40697             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
40698             this.tb.el.show();
40699             // update name
40700             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
40701             
40702             
40703             // update attributes
40704             if (this.tb.fields) {
40705                 this.tb.fields.each(function(e) {
40706                    e.setValue(sel.getAttribute(e.name));
40707                 });
40708             }
40709             
40710             // update styles
40711             var st = this.tb.fields.item(0);
40712             st.store.removeAll();
40713             var cn = sel.className.split(/\s+/);
40714             
40715             var avs = [];
40716             if (this.styles['*']) {
40717                 
40718                 Roo.each(this.styles['*'], function(v) {
40719                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
40720                 });
40721             }
40722             if (this.styles[tn]) { 
40723                 Roo.each(this.styles[tn], function(v) {
40724                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
40725                 });
40726             }
40727             
40728             st.store.loadData(avs);
40729             st.collapse();
40730             st.setValue(cn);
40731             
40732             // flag our selected Node.
40733             this.tb.selectedNode = sel;
40734            
40735            
40736             Roo.menu.MenuMgr.hideAll();
40737
40738         }
40739         
40740         if (!updateFooter) {
40741             return;
40742         }
40743         // update the footer
40744         //
40745         var html = '';
40746         
40747         this.footerEls = ans.reverse();
40748         Roo.each(this.footerEls, function(a,i) {
40749             if (!a) { return; }
40750             html += html.length ? ' &gt; '  :  '';
40751             
40752             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
40753             
40754         });
40755        
40756         // 
40757         var sz = this.footDisp.up('td').getSize();
40758         this.footDisp.dom.style.width = (sz.width -10) + 'px';
40759         this.footDisp.dom.style.marginLeft = '5px';
40760         
40761         this.footDisp.dom.style.overflow = 'hidden';
40762         
40763         this.footDisp.dom.innerHTML = html;
40764             
40765         //this.editorsyncValue();
40766     },
40767    
40768        
40769     // private
40770     onDestroy : function(){
40771         if(this.rendered){
40772             
40773             this.tb.items.each(function(item){
40774                 if(item.menu){
40775                     item.menu.removeAll();
40776                     if(item.menu.el){
40777                         item.menu.el.destroy();
40778                     }
40779                 }
40780                 item.destroy();
40781             });
40782              
40783         }
40784     },
40785     onFirstFocus: function() {
40786         // need to do this for all the toolbars..
40787         this.tb.items.each(function(item){
40788            item.enable();
40789         });
40790     },
40791     buildToolbar: function(tlist, nm)
40792     {
40793         var editor = this.editor;
40794          // create a new element.
40795         var wdiv = editor.wrap.createChild({
40796                 tag: 'div'
40797             }, editor.wrap.dom.firstChild.nextSibling, true);
40798         
40799        
40800         var tb = new Roo.Toolbar(wdiv);
40801         // add the name..
40802         
40803         tb.add(nm+ ":&nbsp;");
40804         
40805         // styles...
40806         if (this.styles) {
40807             
40808             // this needs a multi-select checkbox...
40809             tb.addField( new Roo.form.ComboBox({
40810                 store: new Roo.data.SimpleStore({
40811                     id : 'val',
40812                     fields: ['val', 'selected'],
40813                     data : [] 
40814                 }),
40815                 name : 'className',
40816                 displayField:'val',
40817                 typeAhead: false,
40818                 mode: 'local',
40819                 editable : false,
40820                 triggerAction: 'all',
40821                 emptyText:'Select Style',
40822                 selectOnFocus:true,
40823                 width: 130,
40824                 listeners : {
40825                     'select': function(c, r, i) {
40826                         // initial support only for on class per el..
40827                         tb.selectedNode.className =  r ? r.get('val') : '';
40828                     }
40829                 }
40830     
40831             }));
40832         }
40833             
40834         
40835         
40836         for (var i in tlist) {
40837             
40838             var item = tlist[i];
40839             tb.add(item.title + ":&nbsp;");
40840             
40841             
40842             
40843             
40844             if (item.opts) {
40845                 // opts == pulldown..
40846                 tb.addField( new Roo.form.ComboBox({
40847                     store: new Roo.data.SimpleStore({
40848                         id : 'val',
40849                         fields: ['val'],
40850                         data : item.opts  
40851                     }),
40852                     name : i,
40853                     displayField:'val',
40854                     typeAhead: false,
40855                     mode: 'local',
40856                     editable : false,
40857                     triggerAction: 'all',
40858                     emptyText:'Select',
40859                     selectOnFocus:true,
40860                     width: item.width ? item.width  : 130,
40861                     listeners : {
40862                         'select': function(c, r, i) {
40863                             tb.selectedNode.setAttribute(c.name, r.get('val'));
40864                         }
40865                     }
40866
40867                 }));
40868                 continue;
40869                     
40870                  
40871                 
40872                 tb.addField( new Roo.form.TextField({
40873                     name: i,
40874                     width: 100,
40875                     //allowBlank:false,
40876                     value: ''
40877                 }));
40878                 continue;
40879             }
40880             tb.addField( new Roo.form.TextField({
40881                 name: i,
40882                 width: item.width,
40883                 //allowBlank:true,
40884                 value: '',
40885                 listeners: {
40886                     'change' : function(f, nv, ov) {
40887                         tb.selectedNode.setAttribute(f.name, nv);
40888                     }
40889                 }
40890             }));
40891              
40892         }
40893         tb.el.on('click', function(e){
40894             e.preventDefault(); // what does this do?
40895         });
40896         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
40897         tb.el.hide();
40898         tb.name = nm;
40899         // dont need to disable them... as they will get hidden
40900         return tb;
40901          
40902         
40903     },
40904     buildFooter : function()
40905     {
40906         
40907         var fel = this.editor.wrap.createChild();
40908         this.footer = new Roo.Toolbar(fel);
40909         // toolbar has scrolly on left / right?
40910         var footDisp= new Roo.Toolbar.Fill();
40911         var _t = this;
40912         this.footer.add(
40913             {
40914                 text : '&lt;',
40915                 xtype: 'Button',
40916                 handler : function() {
40917                     _t.footDisp.scrollTo('left',0,true)
40918                 }
40919             }
40920         );
40921         this.footer.add( footDisp );
40922         this.footer.add( 
40923             {
40924                 text : '&gt;',
40925                 xtype: 'Button',
40926                 handler : function() {
40927                     // no animation..
40928                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
40929                 }
40930             }
40931         );
40932         var fel = Roo.get(footDisp.el);
40933         fel.addClass('x-editor-context');
40934         this.footDispWrap = fel; 
40935         this.footDispWrap.overflow  = 'hidden';
40936         
40937         this.footDisp = fel.createChild();
40938         this.footDispWrap.on('click', this.onContextClick, this)
40939         
40940         
40941     },
40942     onContextClick : function (ev,dom)
40943     {
40944         ev.preventDefault();
40945         var  cn = dom.className;
40946         Roo.log(cn);
40947         if (!cn.match(/x-ed-loc-/)) {
40948             return;
40949         }
40950         var n = cn.split('-').pop();
40951         var ans = this.footerEls;
40952         var sel = ans[n];
40953         
40954          // pick
40955         var range = this.editor.createRange();
40956         
40957         range.selectNodeContents(sel);
40958         //range.selectNode(sel);
40959         
40960         
40961         var selection = this.editor.getSelection();
40962         selection.removeAllRanges();
40963         selection.addRange(range);
40964         
40965         
40966         
40967         this.updateToolbar(null, null, sel);
40968         
40969         
40970     }
40971     
40972     
40973     
40974     
40975     
40976 });
40977
40978
40979
40980
40981
40982 /*
40983  * Based on:
40984  * Ext JS Library 1.1.1
40985  * Copyright(c) 2006-2007, Ext JS, LLC.
40986  *
40987  * Originally Released Under LGPL - original licence link has changed is not relivant.
40988  *
40989  * Fork - LGPL
40990  * <script type="text/javascript">
40991  */
40992  
40993 /**
40994  * @class Roo.form.BasicForm
40995  * @extends Roo.util.Observable
40996  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
40997  * @constructor
40998  * @param {String/HTMLElement/Roo.Element} el The form element or its id
40999  * @param {Object} config Configuration options
41000  */
41001 Roo.form.BasicForm = function(el, config){
41002     this.allItems = [];
41003     this.childForms = [];
41004     Roo.apply(this, config);
41005     /*
41006      * The Roo.form.Field items in this form.
41007      * @type MixedCollection
41008      */
41009      
41010      
41011     this.items = new Roo.util.MixedCollection(false, function(o){
41012         return o.id || (o.id = Roo.id());
41013     });
41014     this.addEvents({
41015         /**
41016          * @event beforeaction
41017          * Fires before any action is performed. Return false to cancel the action.
41018          * @param {Form} this
41019          * @param {Action} action The action to be performed
41020          */
41021         beforeaction: true,
41022         /**
41023          * @event actionfailed
41024          * Fires when an action fails.
41025          * @param {Form} this
41026          * @param {Action} action The action that failed
41027          */
41028         actionfailed : true,
41029         /**
41030          * @event actioncomplete
41031          * Fires when an action is completed.
41032          * @param {Form} this
41033          * @param {Action} action The action that completed
41034          */
41035         actioncomplete : true
41036     });
41037     if(el){
41038         this.initEl(el);
41039     }
41040     Roo.form.BasicForm.superclass.constructor.call(this);
41041 };
41042
41043 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
41044     /**
41045      * @cfg {String} method
41046      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
41047      */
41048     /**
41049      * @cfg {DataReader} reader
41050      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
41051      * This is optional as there is built-in support for processing JSON.
41052      */
41053     /**
41054      * @cfg {DataReader} errorReader
41055      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
41056      * This is completely optional as there is built-in support for processing JSON.
41057      */
41058     /**
41059      * @cfg {String} url
41060      * The URL to use for form actions if one isn't supplied in the action options.
41061      */
41062     /**
41063      * @cfg {Boolean} fileUpload
41064      * Set to true if this form is a file upload.
41065      */
41066      
41067     /**
41068      * @cfg {Object} baseParams
41069      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
41070      */
41071      /**
41072      
41073     /**
41074      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
41075      */
41076     timeout: 30,
41077
41078     // private
41079     activeAction : null,
41080
41081     /**
41082      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
41083      * or setValues() data instead of when the form was first created.
41084      */
41085     trackResetOnLoad : false,
41086     
41087     
41088     /**
41089      * childForms - used for multi-tab forms
41090      * @type {Array}
41091      */
41092     childForms : false,
41093     
41094     /**
41095      * allItems - full list of fields.
41096      * @type {Array}
41097      */
41098     allItems : false,
41099     
41100     /**
41101      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
41102      * element by passing it or its id or mask the form itself by passing in true.
41103      * @type Mixed
41104      */
41105     waitMsgTarget : false,
41106
41107     // private
41108     initEl : function(el){
41109         this.el = Roo.get(el);
41110         this.id = this.el.id || Roo.id();
41111         this.el.on('submit', this.onSubmit, this);
41112         this.el.addClass('x-form');
41113     },
41114
41115     // private
41116     onSubmit : function(e){
41117         e.stopEvent();
41118     },
41119
41120     /**
41121      * Returns true if client-side validation on the form is successful.
41122      * @return Boolean
41123      */
41124     isValid : function(){
41125         var valid = true;
41126         this.items.each(function(f){
41127            if(!f.validate()){
41128                valid = false;
41129            }
41130         });
41131         return valid;
41132     },
41133
41134     /**
41135      * Returns true if any fields in this form have changed since their original load.
41136      * @return Boolean
41137      */
41138     isDirty : function(){
41139         var dirty = false;
41140         this.items.each(function(f){
41141            if(f.isDirty()){
41142                dirty = true;
41143                return false;
41144            }
41145         });
41146         return dirty;
41147     },
41148
41149     /**
41150      * Performs a predefined action (submit or load) or custom actions you define on this form.
41151      * @param {String} actionName The name of the action type
41152      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
41153      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
41154      * accept other config options):
41155      * <pre>
41156 Property          Type             Description
41157 ----------------  ---------------  ----------------------------------------------------------------------------------
41158 url               String           The url for the action (defaults to the form's url)
41159 method            String           The form method to use (defaults to the form's method, or POST if not defined)
41160 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
41161 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
41162                                    validate the form on the client (defaults to false)
41163      * </pre>
41164      * @return {BasicForm} this
41165      */
41166     doAction : function(action, options){
41167         if(typeof action == 'string'){
41168             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
41169         }
41170         if(this.fireEvent('beforeaction', this, action) !== false){
41171             this.beforeAction(action);
41172             action.run.defer(100, action);
41173         }
41174         return this;
41175     },
41176
41177     /**
41178      * Shortcut to do a submit action.
41179      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
41180      * @return {BasicForm} this
41181      */
41182     submit : function(options){
41183         this.doAction('submit', options);
41184         return this;
41185     },
41186
41187     /**
41188      * Shortcut to do a load action.
41189      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
41190      * @return {BasicForm} this
41191      */
41192     load : function(options){
41193         this.doAction('load', options);
41194         return this;
41195     },
41196
41197     /**
41198      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
41199      * @param {Record} record The record to edit
41200      * @return {BasicForm} this
41201      */
41202     updateRecord : function(record){
41203         record.beginEdit();
41204         var fs = record.fields;
41205         fs.each(function(f){
41206             var field = this.findField(f.name);
41207             if(field){
41208                 record.set(f.name, field.getValue());
41209             }
41210         }, this);
41211         record.endEdit();
41212         return this;
41213     },
41214
41215     /**
41216      * Loads an Roo.data.Record into this form.
41217      * @param {Record} record The record to load
41218      * @return {BasicForm} this
41219      */
41220     loadRecord : function(record){
41221         this.setValues(record.data);
41222         return this;
41223     },
41224
41225     // private
41226     beforeAction : function(action){
41227         var o = action.options;
41228         
41229        
41230         if(this.waitMsgTarget === true){
41231             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
41232         }else if(this.waitMsgTarget){
41233             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
41234             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
41235         }else {
41236             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
41237         }
41238          
41239     },
41240
41241     // private
41242     afterAction : function(action, success){
41243         this.activeAction = null;
41244         var o = action.options;
41245         
41246         if(this.waitMsgTarget === true){
41247             this.el.unmask();
41248         }else if(this.waitMsgTarget){
41249             this.waitMsgTarget.unmask();
41250         }else{
41251             Roo.MessageBox.updateProgress(1);
41252             Roo.MessageBox.hide();
41253         }
41254          
41255         if(success){
41256             if(o.reset){
41257                 this.reset();
41258             }
41259             Roo.callback(o.success, o.scope, [this, action]);
41260             this.fireEvent('actioncomplete', this, action);
41261             
41262         }else{
41263             Roo.callback(o.failure, o.scope, [this, action]);
41264             // show an error message if no failed handler is set..
41265             if (!this.hasListener('actionfailed')) {
41266                 Roo.MessageBox.alert("Error", "Saving Failed, please check your entries");
41267             }
41268             
41269             this.fireEvent('actionfailed', this, action);
41270         }
41271         
41272     },
41273
41274     /**
41275      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
41276      * @param {String} id The value to search for
41277      * @return Field
41278      */
41279     findField : function(id){
41280         var field = this.items.get(id);
41281         if(!field){
41282             this.items.each(function(f){
41283                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
41284                     field = f;
41285                     return false;
41286                 }
41287             });
41288         }
41289         return field || null;
41290     },
41291
41292     /**
41293      * Add a secondary form to this one, 
41294      * Used to provide tabbed forms. One form is primary, with hidden values 
41295      * which mirror the elements from the other forms.
41296      * 
41297      * @param {Roo.form.Form} form to add.
41298      * 
41299      */
41300     addForm : function(form)
41301     {
41302        
41303         if (this.childForms.indexOf(form) > -1) {
41304             // already added..
41305             return;
41306         }
41307         this.childForms.push(form);
41308         var n = '';
41309         Roo.each(form.allItems, function (fe) {
41310             
41311             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
41312             if (this.findField(n)) { // already added..
41313                 return;
41314             }
41315             var add = new Roo.form.Hidden({
41316                 name : n
41317             });
41318             add.render(this.el);
41319             
41320             this.add( add );
41321         }, this);
41322         
41323     },
41324     /**
41325      * Mark fields in this form invalid in bulk.
41326      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
41327      * @return {BasicForm} this
41328      */
41329     markInvalid : function(errors){
41330         if(errors instanceof Array){
41331             for(var i = 0, len = errors.length; i < len; i++){
41332                 var fieldError = errors[i];
41333                 var f = this.findField(fieldError.id);
41334                 if(f){
41335                     f.markInvalid(fieldError.msg);
41336                 }
41337             }
41338         }else{
41339             var field, id;
41340             for(id in errors){
41341                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
41342                     field.markInvalid(errors[id]);
41343                 }
41344             }
41345         }
41346         Roo.each(this.childForms || [], function (f) {
41347             f.markInvalid(errors);
41348         });
41349         
41350         return this;
41351     },
41352
41353     /**
41354      * Set values for fields in this form in bulk.
41355      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
41356      * @return {BasicForm} this
41357      */
41358     setValues : function(values){
41359         if(values instanceof Array){ // array of objects
41360             for(var i = 0, len = values.length; i < len; i++){
41361                 var v = values[i];
41362                 var f = this.findField(v.id);
41363                 if(f){
41364                     f.setValue(v.value);
41365                     if(this.trackResetOnLoad){
41366                         f.originalValue = f.getValue();
41367                     }
41368                 }
41369             }
41370         }else{ // object hash
41371             var field, id;
41372             for(id in values){
41373                 if(typeof values[id] != 'function' && (field = this.findField(id))){
41374                     
41375                     if (field.setFromData && 
41376                         field.valueField && 
41377                         field.displayField &&
41378                         // combos' with local stores can 
41379                         // be queried via setValue()
41380                         // to set their value..
41381                         (field.store && !field.store.isLocal)
41382                         ) {
41383                         // it's a combo
41384                         var sd = { };
41385                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
41386                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
41387                         field.setFromData(sd);
41388                         
41389                     } else {
41390                         field.setValue(values[id]);
41391                     }
41392                     
41393                     
41394                     if(this.trackResetOnLoad){
41395                         field.originalValue = field.getValue();
41396                     }
41397                 }
41398             }
41399         }
41400          
41401         Roo.each(this.childForms || [], function (f) {
41402             f.setValues(values);
41403         });
41404                 
41405         return this;
41406     },
41407
41408     /**
41409      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
41410      * they are returned as an array.
41411      * @param {Boolean} asString
41412      * @return {Object}
41413      */
41414     getValues : function(asString){
41415         if (this.childForms) {
41416             // copy values from the child forms
41417             Roo.each(this.childForms, function (f) {
41418                 this.setValues(f.getValues());
41419             }, this);
41420         }
41421         
41422         
41423         
41424         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
41425         if(asString === true){
41426             return fs;
41427         }
41428         return Roo.urlDecode(fs);
41429     },
41430     
41431     /**
41432      * Returns the fields in this form as an object with key/value pairs. 
41433      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
41434      * @return {Object}
41435      */
41436     getFieldValues : function()
41437     {
41438         if (this.childForms) {
41439             // copy values from the child forms
41440             Roo.each(this.childForms, function (f) {
41441                 this.setValues(f.getValues());
41442             }, this);
41443         }
41444         
41445         var ret = {};
41446         this.items.each(function(f){
41447             if (!f.getName()) {
41448                 return;
41449             }
41450             var v = f.getValue();
41451             if ((typeof(v) == 'object') && f.getRawValue) {
41452                 v = f.getRawValue() ; // dates..
41453             }
41454             ret[f.getName()] = v;
41455         });
41456         
41457         return ret;
41458     },
41459
41460     /**
41461      * Clears all invalid messages in this form.
41462      * @return {BasicForm} this
41463      */
41464     clearInvalid : function(){
41465         this.items.each(function(f){
41466            f.clearInvalid();
41467         });
41468         
41469         Roo.each(this.childForms || [], function (f) {
41470             f.clearInvalid();
41471         });
41472         
41473         
41474         return this;
41475     },
41476
41477     /**
41478      * Resets this form.
41479      * @return {BasicForm} this
41480      */
41481     reset : function(){
41482         this.items.each(function(f){
41483             f.reset();
41484         });
41485         
41486         Roo.each(this.childForms || [], function (f) {
41487             f.reset();
41488         });
41489        
41490         
41491         return this;
41492     },
41493
41494     /**
41495      * Add Roo.form components to this form.
41496      * @param {Field} field1
41497      * @param {Field} field2 (optional)
41498      * @param {Field} etc (optional)
41499      * @return {BasicForm} this
41500      */
41501     add : function(){
41502         this.items.addAll(Array.prototype.slice.call(arguments, 0));
41503         return this;
41504     },
41505
41506
41507     /**
41508      * Removes a field from the items collection (does NOT remove its markup).
41509      * @param {Field} field
41510      * @return {BasicForm} this
41511      */
41512     remove : function(field){
41513         this.items.remove(field);
41514         return this;
41515     },
41516
41517     /**
41518      * Looks at the fields in this form, checks them for an id attribute,
41519      * and calls applyTo on the existing dom element with that id.
41520      * @return {BasicForm} this
41521      */
41522     render : function(){
41523         this.items.each(function(f){
41524             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
41525                 f.applyTo(f.id);
41526             }
41527         });
41528         return this;
41529     },
41530
41531     /**
41532      * Calls {@link Ext#apply} for all fields in this form with the passed object.
41533      * @param {Object} values
41534      * @return {BasicForm} this
41535      */
41536     applyToFields : function(o){
41537         this.items.each(function(f){
41538            Roo.apply(f, o);
41539         });
41540         return this;
41541     },
41542
41543     /**
41544      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
41545      * @param {Object} values
41546      * @return {BasicForm} this
41547      */
41548     applyIfToFields : function(o){
41549         this.items.each(function(f){
41550            Roo.applyIf(f, o);
41551         });
41552         return this;
41553     }
41554 });
41555
41556 // back compat
41557 Roo.BasicForm = Roo.form.BasicForm;/*
41558  * Based on:
41559  * Ext JS Library 1.1.1
41560  * Copyright(c) 2006-2007, Ext JS, LLC.
41561  *
41562  * Originally Released Under LGPL - original licence link has changed is not relivant.
41563  *
41564  * Fork - LGPL
41565  * <script type="text/javascript">
41566  */
41567
41568 /**
41569  * @class Roo.form.Form
41570  * @extends Roo.form.BasicForm
41571  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
41572  * @constructor
41573  * @param {Object} config Configuration options
41574  */
41575 Roo.form.Form = function(config){
41576     var xitems =  [];
41577     if (config.items) {
41578         xitems = config.items;
41579         delete config.items;
41580     }
41581    
41582     
41583     Roo.form.Form.superclass.constructor.call(this, null, config);
41584     this.url = this.url || this.action;
41585     if(!this.root){
41586         this.root = new Roo.form.Layout(Roo.applyIf({
41587             id: Roo.id()
41588         }, config));
41589     }
41590     this.active = this.root;
41591     /**
41592      * Array of all the buttons that have been added to this form via {@link addButton}
41593      * @type Array
41594      */
41595     this.buttons = [];
41596     this.allItems = [];
41597     this.addEvents({
41598         /**
41599          * @event clientvalidation
41600          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
41601          * @param {Form} this
41602          * @param {Boolean} valid true if the form has passed client-side validation
41603          */
41604         clientvalidation: true,
41605         /**
41606          * @event rendered
41607          * Fires when the form is rendered
41608          * @param {Roo.form.Form} form
41609          */
41610         rendered : true
41611     });
41612     
41613     if (this.progressUrl) {
41614             // push a hidden field onto the list of fields..
41615             this.addxtype( {
41616                     xns: Roo.form, 
41617                     xtype : 'Hidden', 
41618                     name : 'UPLOAD_IDENTIFIER' 
41619             });
41620         }
41621         
41622     
41623     Roo.each(xitems, this.addxtype, this);
41624     
41625     
41626     
41627 };
41628
41629 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
41630     /**
41631      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
41632      */
41633     /**
41634      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
41635      */
41636     /**
41637      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
41638      */
41639     buttonAlign:'center',
41640
41641     /**
41642      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
41643      */
41644     minButtonWidth:75,
41645
41646     /**
41647      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
41648      * This property cascades to child containers if not set.
41649      */
41650     labelAlign:'left',
41651
41652     /**
41653      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
41654      * fires a looping event with that state. This is required to bind buttons to the valid
41655      * state using the config value formBind:true on the button.
41656      */
41657     monitorValid : false,
41658
41659     /**
41660      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
41661      */
41662     monitorPoll : 200,
41663     
41664     /**
41665      * @cfg {String} progressUrl - Url to return progress data 
41666      */
41667     
41668     progressUrl : false,
41669   
41670     /**
41671      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
41672      * fields are added and the column is closed. If no fields are passed the column remains open
41673      * until end() is called.
41674      * @param {Object} config The config to pass to the column
41675      * @param {Field} field1 (optional)
41676      * @param {Field} field2 (optional)
41677      * @param {Field} etc (optional)
41678      * @return Column The column container object
41679      */
41680     column : function(c){
41681         var col = new Roo.form.Column(c);
41682         this.start(col);
41683         if(arguments.length > 1){ // duplicate code required because of Opera
41684             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
41685             this.end();
41686         }
41687         return col;
41688     },
41689
41690     /**
41691      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
41692      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
41693      * until end() is called.
41694      * @param {Object} config The config to pass to the fieldset
41695      * @param {Field} field1 (optional)
41696      * @param {Field} field2 (optional)
41697      * @param {Field} etc (optional)
41698      * @return FieldSet The fieldset container object
41699      */
41700     fieldset : function(c){
41701         var fs = new Roo.form.FieldSet(c);
41702         this.start(fs);
41703         if(arguments.length > 1){ // duplicate code required because of Opera
41704             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
41705             this.end();
41706         }
41707         return fs;
41708     },
41709
41710     /**
41711      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
41712      * fields are added and the container is closed. If no fields are passed the container remains open
41713      * until end() is called.
41714      * @param {Object} config The config to pass to the Layout
41715      * @param {Field} field1 (optional)
41716      * @param {Field} field2 (optional)
41717      * @param {Field} etc (optional)
41718      * @return Layout The container object
41719      */
41720     container : function(c){
41721         var l = new Roo.form.Layout(c);
41722         this.start(l);
41723         if(arguments.length > 1){ // duplicate code required because of Opera
41724             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
41725             this.end();
41726         }
41727         return l;
41728     },
41729
41730     /**
41731      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
41732      * @param {Object} container A Roo.form.Layout or subclass of Layout
41733      * @return {Form} this
41734      */
41735     start : function(c){
41736         // cascade label info
41737         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
41738         this.active.stack.push(c);
41739         c.ownerCt = this.active;
41740         this.active = c;
41741         return this;
41742     },
41743
41744     /**
41745      * Closes the current open container
41746      * @return {Form} this
41747      */
41748     end : function(){
41749         if(this.active == this.root){
41750             return this;
41751         }
41752         this.active = this.active.ownerCt;
41753         return this;
41754     },
41755
41756     /**
41757      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
41758      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
41759      * as the label of the field.
41760      * @param {Field} field1
41761      * @param {Field} field2 (optional)
41762      * @param {Field} etc. (optional)
41763      * @return {Form} this
41764      */
41765     add : function(){
41766         this.active.stack.push.apply(this.active.stack, arguments);
41767         this.allItems.push.apply(this.allItems,arguments);
41768         var r = [];
41769         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
41770             if(a[i].isFormField){
41771                 r.push(a[i]);
41772             }
41773         }
41774         if(r.length > 0){
41775             Roo.form.Form.superclass.add.apply(this, r);
41776         }
41777         return this;
41778     },
41779     
41780
41781     
41782     
41783     
41784      /**
41785      * Find any element that has been added to a form, using it's ID or name
41786      * This can include framesets, columns etc. along with regular fields..
41787      * @param {String} id - id or name to find.
41788      
41789      * @return {Element} e - or false if nothing found.
41790      */
41791     findbyId : function(id)
41792     {
41793         var ret = false;
41794         if (!id) {
41795             return ret;
41796         }
41797         Roo.each(this.allItems, function(f){
41798             if (f.id == id || f.name == id ){
41799                 ret = f;
41800                 return false;
41801             }
41802         });
41803         return ret;
41804     },
41805
41806     
41807     
41808     /**
41809      * Render this form into the passed container. This should only be called once!
41810      * @param {String/HTMLElement/Element} container The element this component should be rendered into
41811      * @return {Form} this
41812      */
41813     render : function(ct)
41814     {
41815         
41816         
41817         
41818         ct = Roo.get(ct);
41819         var o = this.autoCreate || {
41820             tag: 'form',
41821             method : this.method || 'POST',
41822             id : this.id || Roo.id()
41823         };
41824         this.initEl(ct.createChild(o));
41825
41826         this.root.render(this.el);
41827         
41828        
41829              
41830         this.items.each(function(f){
41831             f.render('x-form-el-'+f.id);
41832         });
41833
41834         if(this.buttons.length > 0){
41835             // tables are required to maintain order and for correct IE layout
41836             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
41837                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
41838                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
41839             }}, null, true);
41840             var tr = tb.getElementsByTagName('tr')[0];
41841             for(var i = 0, len = this.buttons.length; i < len; i++) {
41842                 var b = this.buttons[i];
41843                 var td = document.createElement('td');
41844                 td.className = 'x-form-btn-td';
41845                 b.render(tr.appendChild(td));
41846             }
41847         }
41848         if(this.monitorValid){ // initialize after render
41849             this.startMonitoring();
41850         }
41851         this.fireEvent('rendered', this);
41852         return this;
41853     },
41854
41855     /**
41856      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
41857      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
41858      * object or a valid Roo.DomHelper element config
41859      * @param {Function} handler The function called when the button is clicked
41860      * @param {Object} scope (optional) The scope of the handler function
41861      * @return {Roo.Button}
41862      */
41863     addButton : function(config, handler, scope){
41864         var bc = {
41865             handler: handler,
41866             scope: scope,
41867             minWidth: this.minButtonWidth,
41868             hideParent:true
41869         };
41870         if(typeof config == "string"){
41871             bc.text = config;
41872         }else{
41873             Roo.apply(bc, config);
41874         }
41875         var btn = new Roo.Button(null, bc);
41876         this.buttons.push(btn);
41877         return btn;
41878     },
41879
41880      /**
41881      * Adds a series of form elements (using the xtype property as the factory method.
41882      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
41883      * @param {Object} config 
41884      */
41885     
41886     addxtype : function()
41887     {
41888         var ar = Array.prototype.slice.call(arguments, 0);
41889         var ret = false;
41890         for(var i = 0; i < ar.length; i++) {
41891             if (!ar[i]) {
41892                 continue; // skip -- if this happends something invalid got sent, we 
41893                 // should ignore it, as basically that interface element will not show up
41894                 // and that should be pretty obvious!!
41895             }
41896             
41897             if (Roo.form[ar[i].xtype]) {
41898                 ar[i].form = this;
41899                 var fe = Roo.factory(ar[i], Roo.form);
41900                 if (!ret) {
41901                     ret = fe;
41902                 }
41903                 fe.form = this;
41904                 if (fe.store) {
41905                     fe.store.form = this;
41906                 }
41907                 if (fe.isLayout) {  
41908                          
41909                     this.start(fe);
41910                     this.allItems.push(fe);
41911                     if (fe.items && fe.addxtype) {
41912                         fe.addxtype.apply(fe, fe.items);
41913                         delete fe.items;
41914                     }
41915                      this.end();
41916                     continue;
41917                 }
41918                 
41919                 
41920                  
41921                 this.add(fe);
41922               //  console.log('adding ' + ar[i].xtype);
41923             }
41924             if (ar[i].xtype == 'Button') {  
41925                 //console.log('adding button');
41926                 //console.log(ar[i]);
41927                 this.addButton(ar[i]);
41928                 this.allItems.push(fe);
41929                 continue;
41930             }
41931             
41932             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
41933                 alert('end is not supported on xtype any more, use items');
41934             //    this.end();
41935             //    //console.log('adding end');
41936             }
41937             
41938         }
41939         return ret;
41940     },
41941     
41942     /**
41943      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
41944      * option "monitorValid"
41945      */
41946     startMonitoring : function(){
41947         if(!this.bound){
41948             this.bound = true;
41949             Roo.TaskMgr.start({
41950                 run : this.bindHandler,
41951                 interval : this.monitorPoll || 200,
41952                 scope: this
41953             });
41954         }
41955     },
41956
41957     /**
41958      * Stops monitoring of the valid state of this form
41959      */
41960     stopMonitoring : function(){
41961         this.bound = false;
41962     },
41963
41964     // private
41965     bindHandler : function(){
41966         if(!this.bound){
41967             return false; // stops binding
41968         }
41969         var valid = true;
41970         this.items.each(function(f){
41971             if(!f.isValid(true)){
41972                 valid = false;
41973                 return false;
41974             }
41975         });
41976         for(var i = 0, len = this.buttons.length; i < len; i++){
41977             var btn = this.buttons[i];
41978             if(btn.formBind === true && btn.disabled === valid){
41979                 btn.setDisabled(!valid);
41980             }
41981         }
41982         this.fireEvent('clientvalidation', this, valid);
41983     }
41984     
41985     
41986     
41987     
41988     
41989     
41990     
41991     
41992 });
41993
41994
41995 // back compat
41996 Roo.Form = Roo.form.Form;
41997 /*
41998  * Based on:
41999  * Ext JS Library 1.1.1
42000  * Copyright(c) 2006-2007, Ext JS, LLC.
42001  *
42002  * Originally Released Under LGPL - original licence link has changed is not relivant.
42003  *
42004  * Fork - LGPL
42005  * <script type="text/javascript">
42006  */
42007  
42008  /**
42009  * @class Roo.form.Action
42010  * Internal Class used to handle form actions
42011  * @constructor
42012  * @param {Roo.form.BasicForm} el The form element or its id
42013  * @param {Object} config Configuration options
42014  */
42015  
42016  
42017 // define the action interface
42018 Roo.form.Action = function(form, options){
42019     this.form = form;
42020     this.options = options || {};
42021 };
42022 /**
42023  * Client Validation Failed
42024  * @const 
42025  */
42026 Roo.form.Action.CLIENT_INVALID = 'client';
42027 /**
42028  * Server Validation Failed
42029  * @const 
42030  */
42031  Roo.form.Action.SERVER_INVALID = 'server';
42032  /**
42033  * Connect to Server Failed
42034  * @const 
42035  */
42036 Roo.form.Action.CONNECT_FAILURE = 'connect';
42037 /**
42038  * Reading Data from Server Failed
42039  * @const 
42040  */
42041 Roo.form.Action.LOAD_FAILURE = 'load';
42042
42043 Roo.form.Action.prototype = {
42044     type : 'default',
42045     failureType : undefined,
42046     response : undefined,
42047     result : undefined,
42048
42049     // interface method
42050     run : function(options){
42051
42052     },
42053
42054     // interface method
42055     success : function(response){
42056
42057     },
42058
42059     // interface method
42060     handleResponse : function(response){
42061
42062     },
42063
42064     // default connection failure
42065     failure : function(response){
42066         
42067         this.response = response;
42068         this.failureType = Roo.form.Action.CONNECT_FAILURE;
42069         this.form.afterAction(this, false);
42070     },
42071
42072     processResponse : function(response){
42073         this.response = response;
42074         if(!response.responseText){
42075             return true;
42076         }
42077         this.result = this.handleResponse(response);
42078         return this.result;
42079     },
42080
42081     // utility functions used internally
42082     getUrl : function(appendParams){
42083         var url = this.options.url || this.form.url || this.form.el.dom.action;
42084         if(appendParams){
42085             var p = this.getParams();
42086             if(p){
42087                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
42088             }
42089         }
42090         return url;
42091     },
42092
42093     getMethod : function(){
42094         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
42095     },
42096
42097     getParams : function(){
42098         var bp = this.form.baseParams;
42099         var p = this.options.params;
42100         if(p){
42101             if(typeof p == "object"){
42102                 p = Roo.urlEncode(Roo.applyIf(p, bp));
42103             }else if(typeof p == 'string' && bp){
42104                 p += '&' + Roo.urlEncode(bp);
42105             }
42106         }else if(bp){
42107             p = Roo.urlEncode(bp);
42108         }
42109         return p;
42110     },
42111
42112     createCallback : function(){
42113         return {
42114             success: this.success,
42115             failure: this.failure,
42116             scope: this,
42117             timeout: (this.form.timeout*1000),
42118             upload: this.form.fileUpload ? this.success : undefined
42119         };
42120     }
42121 };
42122
42123 Roo.form.Action.Submit = function(form, options){
42124     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
42125 };
42126
42127 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
42128     type : 'submit',
42129
42130     haveProgress : false,
42131     uploadComplete : false,
42132     
42133     // uploadProgress indicator.
42134     uploadProgress : function()
42135     {
42136         if (!this.form.progressUrl) {
42137             return;
42138         }
42139         
42140         if (!this.haveProgress) {
42141             Roo.MessageBox.progress("Uploading", "Uploading");
42142         }
42143         if (this.uploadComplete) {
42144            Roo.MessageBox.hide();
42145            return;
42146         }
42147         
42148         this.haveProgress = true;
42149    
42150         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
42151         
42152         var c = new Roo.data.Connection();
42153         c.request({
42154             url : this.form.progressUrl,
42155             params: {
42156                 id : uid
42157             },
42158             method: 'GET',
42159             success : function(req){
42160                //console.log(data);
42161                 var rdata = false;
42162                 var edata;
42163                 try  {
42164                    rdata = Roo.decode(req.responseText)
42165                 } catch (e) {
42166                     Roo.log("Invalid data from server..");
42167                     Roo.log(edata);
42168                     return;
42169                 }
42170                 if (!rdata || !rdata.success) {
42171                     Roo.log(rdata);
42172                     return;
42173                 }
42174                 var data = rdata.data;
42175                 
42176                 if (this.uploadComplete) {
42177                    Roo.MessageBox.hide();
42178                    return;
42179                 }
42180                    
42181                 if (data){
42182                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
42183                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
42184                     );
42185                 }
42186                 this.uploadProgress.defer(2000,this);
42187             },
42188        
42189             failure: function(data) {
42190                 Roo.log('progress url failed ');
42191                 Roo.log(data);
42192             },
42193             scope : this
42194         });
42195            
42196     },
42197     
42198     
42199     run : function()
42200     {
42201         // run get Values on the form, so it syncs any secondary forms.
42202         this.form.getValues();
42203         
42204         var o = this.options;
42205         var method = this.getMethod();
42206         var isPost = method == 'POST';
42207         if(o.clientValidation === false || this.form.isValid()){
42208             
42209             if (this.form.progressUrl) {
42210                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
42211                     (new Date() * 1) + '' + Math.random());
42212                     
42213             } 
42214             
42215             
42216             Roo.Ajax.request(Roo.apply(this.createCallback(), {
42217                 form:this.form.el.dom,
42218                 url:this.getUrl(!isPost),
42219                 method: method,
42220                 params:isPost ? this.getParams() : null,
42221                 isUpload: this.form.fileUpload
42222             }));
42223             
42224             this.uploadProgress();
42225
42226         }else if (o.clientValidation !== false){ // client validation failed
42227             this.failureType = Roo.form.Action.CLIENT_INVALID;
42228             this.form.afterAction(this, false);
42229         }
42230     },
42231
42232     success : function(response)
42233     {
42234         this.uploadComplete= true;
42235         if (this.haveProgress) {
42236             Roo.MessageBox.hide();
42237         }
42238         
42239         
42240         var result = this.processResponse(response);
42241         if(result === true || result.success){
42242             this.form.afterAction(this, true);
42243             return;
42244         }
42245         if(result.errors){
42246             this.form.markInvalid(result.errors);
42247             this.failureType = Roo.form.Action.SERVER_INVALID;
42248         }
42249         this.form.afterAction(this, false);
42250     },
42251     failure : function(response)
42252     {
42253         this.uploadComplete= true;
42254         if (this.haveProgress) {
42255             Roo.MessageBox.hide();
42256         }
42257         
42258         
42259         this.response = response;
42260         this.failureType = Roo.form.Action.CONNECT_FAILURE;
42261         this.form.afterAction(this, false);
42262     },
42263     
42264     handleResponse : function(response){
42265         if(this.form.errorReader){
42266             var rs = this.form.errorReader.read(response);
42267             var errors = [];
42268             if(rs.records){
42269                 for(var i = 0, len = rs.records.length; i < len; i++) {
42270                     var r = rs.records[i];
42271                     errors[i] = r.data;
42272                 }
42273             }
42274             if(errors.length < 1){
42275                 errors = null;
42276             }
42277             return {
42278                 success : rs.success,
42279                 errors : errors
42280             };
42281         }
42282         var ret = false;
42283         try {
42284             ret = Roo.decode(response.responseText);
42285         } catch (e) {
42286             ret = {
42287                 success: false,
42288                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
42289                 errors : []
42290             };
42291         }
42292         return ret;
42293         
42294     }
42295 });
42296
42297
42298 Roo.form.Action.Load = function(form, options){
42299     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
42300     this.reader = this.form.reader;
42301 };
42302
42303 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
42304     type : 'load',
42305
42306     run : function(){
42307         
42308         Roo.Ajax.request(Roo.apply(
42309                 this.createCallback(), {
42310                     method:this.getMethod(),
42311                     url:this.getUrl(false),
42312                     params:this.getParams()
42313         }));
42314     },
42315
42316     success : function(response){
42317         
42318         var result = this.processResponse(response);
42319         if(result === true || !result.success || !result.data){
42320             this.failureType = Roo.form.Action.LOAD_FAILURE;
42321             this.form.afterAction(this, false);
42322             return;
42323         }
42324         this.form.clearInvalid();
42325         this.form.setValues(result.data);
42326         this.form.afterAction(this, true);
42327     },
42328
42329     handleResponse : function(response){
42330         if(this.form.reader){
42331             var rs = this.form.reader.read(response);
42332             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
42333             return {
42334                 success : rs.success,
42335                 data : data
42336             };
42337         }
42338         return Roo.decode(response.responseText);
42339     }
42340 });
42341
42342 Roo.form.Action.ACTION_TYPES = {
42343     'load' : Roo.form.Action.Load,
42344     'submit' : Roo.form.Action.Submit
42345 };/*
42346  * Based on:
42347  * Ext JS Library 1.1.1
42348  * Copyright(c) 2006-2007, Ext JS, LLC.
42349  *
42350  * Originally Released Under LGPL - original licence link has changed is not relivant.
42351  *
42352  * Fork - LGPL
42353  * <script type="text/javascript">
42354  */
42355  
42356 /**
42357  * @class Roo.form.Layout
42358  * @extends Roo.Component
42359  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
42360  * @constructor
42361  * @param {Object} config Configuration options
42362  */
42363 Roo.form.Layout = function(config){
42364     var xitems = [];
42365     if (config.items) {
42366         xitems = config.items;
42367         delete config.items;
42368     }
42369     Roo.form.Layout.superclass.constructor.call(this, config);
42370     this.stack = [];
42371     Roo.each(xitems, this.addxtype, this);
42372      
42373 };
42374
42375 Roo.extend(Roo.form.Layout, Roo.Component, {
42376     /**
42377      * @cfg {String/Object} autoCreate
42378      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
42379      */
42380     /**
42381      * @cfg {String/Object/Function} style
42382      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
42383      * a function which returns such a specification.
42384      */
42385     /**
42386      * @cfg {String} labelAlign
42387      * Valid values are "left," "top" and "right" (defaults to "left")
42388      */
42389     /**
42390      * @cfg {Number} labelWidth
42391      * Fixed width in pixels of all field labels (defaults to undefined)
42392      */
42393     /**
42394      * @cfg {Boolean} clear
42395      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
42396      */
42397     clear : true,
42398     /**
42399      * @cfg {String} labelSeparator
42400      * The separator to use after field labels (defaults to ':')
42401      */
42402     labelSeparator : ':',
42403     /**
42404      * @cfg {Boolean} hideLabels
42405      * True to suppress the display of field labels in this layout (defaults to false)
42406      */
42407     hideLabels : false,
42408
42409     // private
42410     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
42411     
42412     isLayout : true,
42413     
42414     // private
42415     onRender : function(ct, position){
42416         if(this.el){ // from markup
42417             this.el = Roo.get(this.el);
42418         }else {  // generate
42419             var cfg = this.getAutoCreate();
42420             this.el = ct.createChild(cfg, position);
42421         }
42422         if(this.style){
42423             this.el.applyStyles(this.style);
42424         }
42425         if(this.labelAlign){
42426             this.el.addClass('x-form-label-'+this.labelAlign);
42427         }
42428         if(this.hideLabels){
42429             this.labelStyle = "display:none";
42430             this.elementStyle = "padding-left:0;";
42431         }else{
42432             if(typeof this.labelWidth == 'number'){
42433                 this.labelStyle = "width:"+this.labelWidth+"px;";
42434                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
42435             }
42436             if(this.labelAlign == 'top'){
42437                 this.labelStyle = "width:auto;";
42438                 this.elementStyle = "padding-left:0;";
42439             }
42440         }
42441         var stack = this.stack;
42442         var slen = stack.length;
42443         if(slen > 0){
42444             if(!this.fieldTpl){
42445                 var t = new Roo.Template(
42446                     '<div class="x-form-item {5}">',
42447                         '<label for="{0}" style="{2}">{1}{4}</label>',
42448                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
42449                         '</div>',
42450                     '</div><div class="x-form-clear-left"></div>'
42451                 );
42452                 t.disableFormats = true;
42453                 t.compile();
42454                 Roo.form.Layout.prototype.fieldTpl = t;
42455             }
42456             for(var i = 0; i < slen; i++) {
42457                 if(stack[i].isFormField){
42458                     this.renderField(stack[i]);
42459                 }else{
42460                     this.renderComponent(stack[i]);
42461                 }
42462             }
42463         }
42464         if(this.clear){
42465             this.el.createChild({cls:'x-form-clear'});
42466         }
42467     },
42468
42469     // private
42470     renderField : function(f){
42471         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
42472                f.id, //0
42473                f.fieldLabel, //1
42474                f.labelStyle||this.labelStyle||'', //2
42475                this.elementStyle||'', //3
42476                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
42477                f.itemCls||this.itemCls||''  //5
42478        ], true).getPrevSibling());
42479     },
42480
42481     // private
42482     renderComponent : function(c){
42483         c.render(c.isLayout ? this.el : this.el.createChild());    
42484     },
42485     /**
42486      * Adds a object form elements (using the xtype property as the factory method.)
42487      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
42488      * @param {Object} config 
42489      */
42490     addxtype : function(o)
42491     {
42492         // create the lement.
42493         o.form = this.form;
42494         var fe = Roo.factory(o, Roo.form);
42495         this.form.allItems.push(fe);
42496         this.stack.push(fe);
42497         
42498         if (fe.isFormField) {
42499             this.form.items.add(fe);
42500         }
42501          
42502         return fe;
42503     }
42504 });
42505
42506 /**
42507  * @class Roo.form.Column
42508  * @extends Roo.form.Layout
42509  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
42510  * @constructor
42511  * @param {Object} config Configuration options
42512  */
42513 Roo.form.Column = function(config){
42514     Roo.form.Column.superclass.constructor.call(this, config);
42515 };
42516
42517 Roo.extend(Roo.form.Column, Roo.form.Layout, {
42518     /**
42519      * @cfg {Number/String} width
42520      * The fixed width of the column in pixels or CSS value (defaults to "auto")
42521      */
42522     /**
42523      * @cfg {String/Object} autoCreate
42524      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
42525      */
42526
42527     // private
42528     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
42529
42530     // private
42531     onRender : function(ct, position){
42532         Roo.form.Column.superclass.onRender.call(this, ct, position);
42533         if(this.width){
42534             this.el.setWidth(this.width);
42535         }
42536     }
42537 });
42538
42539
42540 /**
42541  * @class Roo.form.Row
42542  * @extends Roo.form.Layout
42543  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
42544  * @constructor
42545  * @param {Object} config Configuration options
42546  */
42547
42548  
42549 Roo.form.Row = function(config){
42550     Roo.form.Row.superclass.constructor.call(this, config);
42551 };
42552  
42553 Roo.extend(Roo.form.Row, Roo.form.Layout, {
42554       /**
42555      * @cfg {Number/String} width
42556      * The fixed width of the column in pixels or CSS value (defaults to "auto")
42557      */
42558     /**
42559      * @cfg {Number/String} height
42560      * The fixed height of the column in pixels or CSS value (defaults to "auto")
42561      */
42562     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
42563     
42564     padWidth : 20,
42565     // private
42566     onRender : function(ct, position){
42567         //console.log('row render');
42568         if(!this.rowTpl){
42569             var t = new Roo.Template(
42570                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
42571                     '<label for="{0}" style="{2}">{1}{4}</label>',
42572                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
42573                     '</div>',
42574                 '</div>'
42575             );
42576             t.disableFormats = true;
42577             t.compile();
42578             Roo.form.Layout.prototype.rowTpl = t;
42579         }
42580         this.fieldTpl = this.rowTpl;
42581         
42582         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
42583         var labelWidth = 100;
42584         
42585         if ((this.labelAlign != 'top')) {
42586             if (typeof this.labelWidth == 'number') {
42587                 labelWidth = this.labelWidth
42588             }
42589             this.padWidth =  20 + labelWidth;
42590             
42591         }
42592         
42593         Roo.form.Column.superclass.onRender.call(this, ct, position);
42594         if(this.width){
42595             this.el.setWidth(this.width);
42596         }
42597         if(this.height){
42598             this.el.setHeight(this.height);
42599         }
42600     },
42601     
42602     // private
42603     renderField : function(f){
42604         f.fieldEl = this.fieldTpl.append(this.el, [
42605                f.id, f.fieldLabel,
42606                f.labelStyle||this.labelStyle||'',
42607                this.elementStyle||'',
42608                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
42609                f.itemCls||this.itemCls||'',
42610                f.width ? f.width + this.padWidth : 160 + this.padWidth
42611        ],true);
42612     }
42613 });
42614  
42615
42616 /**
42617  * @class Roo.form.FieldSet
42618  * @extends Roo.form.Layout
42619  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
42620  * @constructor
42621  * @param {Object} config Configuration options
42622  */
42623 Roo.form.FieldSet = function(config){
42624     Roo.form.FieldSet.superclass.constructor.call(this, config);
42625 };
42626
42627 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
42628     /**
42629      * @cfg {String} legend
42630      * The text to display as the legend for the FieldSet (defaults to '')
42631      */
42632     /**
42633      * @cfg {String/Object} autoCreate
42634      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
42635      */
42636
42637     // private
42638     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
42639
42640     // private
42641     onRender : function(ct, position){
42642         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
42643         if(this.legend){
42644             this.setLegend(this.legend);
42645         }
42646     },
42647
42648     // private
42649     setLegend : function(text){
42650         if(this.rendered){
42651             this.el.child('legend').update(text);
42652         }
42653     }
42654 });/*
42655  * Based on:
42656  * Ext JS Library 1.1.1
42657  * Copyright(c) 2006-2007, Ext JS, LLC.
42658  *
42659  * Originally Released Under LGPL - original licence link has changed is not relivant.
42660  *
42661  * Fork - LGPL
42662  * <script type="text/javascript">
42663  */
42664 /**
42665  * @class Roo.form.VTypes
42666  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
42667  * @singleton
42668  */
42669 Roo.form.VTypes = function(){
42670     // closure these in so they are only created once.
42671     var alpha = /^[a-zA-Z_]+$/;
42672     var alphanum = /^[a-zA-Z0-9_]+$/;
42673     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
42674     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
42675
42676     // All these messages and functions are configurable
42677     return {
42678         /**
42679          * The function used to validate email addresses
42680          * @param {String} value The email address
42681          */
42682         'email' : function(v){
42683             return email.test(v);
42684         },
42685         /**
42686          * The error text to display when the email validation function returns false
42687          * @type String
42688          */
42689         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
42690         /**
42691          * The keystroke filter mask to be applied on email input
42692          * @type RegExp
42693          */
42694         'emailMask' : /[a-z0-9_\.\-@]/i,
42695
42696         /**
42697          * The function used to validate URLs
42698          * @param {String} value The URL
42699          */
42700         'url' : function(v){
42701             return url.test(v);
42702         },
42703         /**
42704          * The error text to display when the url validation function returns false
42705          * @type String
42706          */
42707         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
42708         
42709         /**
42710          * The function used to validate alpha values
42711          * @param {String} value The value
42712          */
42713         'alpha' : function(v){
42714             return alpha.test(v);
42715         },
42716         /**
42717          * The error text to display when the alpha validation function returns false
42718          * @type String
42719          */
42720         'alphaText' : 'This field should only contain letters and _',
42721         /**
42722          * The keystroke filter mask to be applied on alpha input
42723          * @type RegExp
42724          */
42725         'alphaMask' : /[a-z_]/i,
42726
42727         /**
42728          * The function used to validate alphanumeric values
42729          * @param {String} value The value
42730          */
42731         'alphanum' : function(v){
42732             return alphanum.test(v);
42733         },
42734         /**
42735          * The error text to display when the alphanumeric validation function returns false
42736          * @type String
42737          */
42738         'alphanumText' : 'This field should only contain letters, numbers and _',
42739         /**
42740          * The keystroke filter mask to be applied on alphanumeric input
42741          * @type RegExp
42742          */
42743         'alphanumMask' : /[a-z0-9_]/i
42744     };
42745 }();//<script type="text/javascript">
42746
42747 /**
42748  * @class Roo.form.FCKeditor
42749  * @extends Roo.form.TextArea
42750  * Wrapper around the FCKEditor http://www.fckeditor.net
42751  * @constructor
42752  * Creates a new FCKeditor
42753  * @param {Object} config Configuration options
42754  */
42755 Roo.form.FCKeditor = function(config){
42756     Roo.form.FCKeditor.superclass.constructor.call(this, config);
42757     this.addEvents({
42758          /**
42759          * @event editorinit
42760          * Fired when the editor is initialized - you can add extra handlers here..
42761          * @param {FCKeditor} this
42762          * @param {Object} the FCK object.
42763          */
42764         editorinit : true
42765     });
42766     
42767     
42768 };
42769 Roo.form.FCKeditor.editors = { };
42770 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
42771 {
42772     //defaultAutoCreate : {
42773     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
42774     //},
42775     // private
42776     /**
42777      * @cfg {Object} fck options - see fck manual for details.
42778      */
42779     fckconfig : false,
42780     
42781     /**
42782      * @cfg {Object} fck toolbar set (Basic or Default)
42783      */
42784     toolbarSet : 'Basic',
42785     /**
42786      * @cfg {Object} fck BasePath
42787      */ 
42788     basePath : '/fckeditor/',
42789     
42790     
42791     frame : false,
42792     
42793     value : '',
42794     
42795    
42796     onRender : function(ct, position)
42797     {
42798         if(!this.el){
42799             this.defaultAutoCreate = {
42800                 tag: "textarea",
42801                 style:"width:300px;height:60px;",
42802                 autocomplete: "off"
42803             };
42804         }
42805         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
42806         /*
42807         if(this.grow){
42808             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
42809             if(this.preventScrollbars){
42810                 this.el.setStyle("overflow", "hidden");
42811             }
42812             this.el.setHeight(this.growMin);
42813         }
42814         */
42815         //console.log('onrender' + this.getId() );
42816         Roo.form.FCKeditor.editors[this.getId()] = this;
42817          
42818
42819         this.replaceTextarea() ;
42820         
42821     },
42822     
42823     getEditor : function() {
42824         return this.fckEditor;
42825     },
42826     /**
42827      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
42828      * @param {Mixed} value The value to set
42829      */
42830     
42831     
42832     setValue : function(value)
42833     {
42834         //console.log('setValue: ' + value);
42835         
42836         if(typeof(value) == 'undefined') { // not sure why this is happending...
42837             return;
42838         }
42839         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
42840         
42841         //if(!this.el || !this.getEditor()) {
42842         //    this.value = value;
42843             //this.setValue.defer(100,this,[value]);    
42844         //    return;
42845         //} 
42846         
42847         if(!this.getEditor()) {
42848             return;
42849         }
42850         
42851         this.getEditor().SetData(value);
42852         
42853         //
42854
42855     },
42856
42857     /**
42858      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
42859      * @return {Mixed} value The field value
42860      */
42861     getValue : function()
42862     {
42863         
42864         if (this.frame && this.frame.dom.style.display == 'none') {
42865             return Roo.form.FCKeditor.superclass.getValue.call(this);
42866         }
42867         
42868         if(!this.el || !this.getEditor()) {
42869            
42870            // this.getValue.defer(100,this); 
42871             return this.value;
42872         }
42873        
42874         
42875         var value=this.getEditor().GetData();
42876         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
42877         return Roo.form.FCKeditor.superclass.getValue.call(this);
42878         
42879
42880     },
42881
42882     /**
42883      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
42884      * @return {Mixed} value The field value
42885      */
42886     getRawValue : function()
42887     {
42888         if (this.frame && this.frame.dom.style.display == 'none') {
42889             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
42890         }
42891         
42892         if(!this.el || !this.getEditor()) {
42893             //this.getRawValue.defer(100,this); 
42894             return this.value;
42895             return;
42896         }
42897         
42898         
42899         
42900         var value=this.getEditor().GetData();
42901         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
42902         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
42903          
42904     },
42905     
42906     setSize : function(w,h) {
42907         
42908         
42909         
42910         //if (this.frame && this.frame.dom.style.display == 'none') {
42911         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
42912         //    return;
42913         //}
42914         //if(!this.el || !this.getEditor()) {
42915         //    this.setSize.defer(100,this, [w,h]); 
42916         //    return;
42917         //}
42918         
42919         
42920         
42921         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
42922         
42923         this.frame.dom.setAttribute('width', w);
42924         this.frame.dom.setAttribute('height', h);
42925         this.frame.setSize(w,h);
42926         
42927     },
42928     
42929     toggleSourceEdit : function(value) {
42930         
42931       
42932          
42933         this.el.dom.style.display = value ? '' : 'none';
42934         this.frame.dom.style.display = value ?  'none' : '';
42935         
42936     },
42937     
42938     
42939     focus: function(tag)
42940     {
42941         if (this.frame.dom.style.display == 'none') {
42942             return Roo.form.FCKeditor.superclass.focus.call(this);
42943         }
42944         if(!this.el || !this.getEditor()) {
42945             this.focus.defer(100,this, [tag]); 
42946             return;
42947         }
42948         
42949         
42950         
42951         
42952         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
42953         this.getEditor().Focus();
42954         if (tgs.length) {
42955             if (!this.getEditor().Selection.GetSelection()) {
42956                 this.focus.defer(100,this, [tag]); 
42957                 return;
42958             }
42959             
42960             
42961             var r = this.getEditor().EditorDocument.createRange();
42962             r.setStart(tgs[0],0);
42963             r.setEnd(tgs[0],0);
42964             this.getEditor().Selection.GetSelection().removeAllRanges();
42965             this.getEditor().Selection.GetSelection().addRange(r);
42966             this.getEditor().Focus();
42967         }
42968         
42969     },
42970     
42971     
42972     
42973     replaceTextarea : function()
42974     {
42975         if ( document.getElementById( this.getId() + '___Frame' ) )
42976             return ;
42977         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
42978         //{
42979             // We must check the elements firstly using the Id and then the name.
42980         var oTextarea = document.getElementById( this.getId() );
42981         
42982         var colElementsByName = document.getElementsByName( this.getId() ) ;
42983          
42984         oTextarea.style.display = 'none' ;
42985
42986         if ( oTextarea.tabIndex ) {            
42987             this.TabIndex = oTextarea.tabIndex ;
42988         }
42989         
42990         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
42991         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
42992         this.frame = Roo.get(this.getId() + '___Frame')
42993     },
42994     
42995     _getConfigHtml : function()
42996     {
42997         var sConfig = '' ;
42998
42999         for ( var o in this.fckconfig ) {
43000             sConfig += sConfig.length > 0  ? '&amp;' : '';
43001             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
43002         }
43003
43004         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
43005     },
43006     
43007     
43008     _getIFrameHtml : function()
43009     {
43010         var sFile = 'fckeditor.html' ;
43011         /* no idea what this is about..
43012         try
43013         {
43014             if ( (/fcksource=true/i).test( window.top.location.search ) )
43015                 sFile = 'fckeditor.original.html' ;
43016         }
43017         catch (e) { 
43018         */
43019
43020         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
43021         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
43022         
43023         
43024         var html = '<iframe id="' + this.getId() +
43025             '___Frame" src="' + sLink +
43026             '" width="' + this.width +
43027             '" height="' + this.height + '"' +
43028             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
43029             ' frameborder="0" scrolling="no"></iframe>' ;
43030
43031         return html ;
43032     },
43033     
43034     _insertHtmlBefore : function( html, element )
43035     {
43036         if ( element.insertAdjacentHTML )       {
43037             // IE
43038             element.insertAdjacentHTML( 'beforeBegin', html ) ;
43039         } else { // Gecko
43040             var oRange = document.createRange() ;
43041             oRange.setStartBefore( element ) ;
43042             var oFragment = oRange.createContextualFragment( html );
43043             element.parentNode.insertBefore( oFragment, element ) ;
43044         }
43045     }
43046     
43047     
43048   
43049     
43050     
43051     
43052     
43053
43054 });
43055
43056 //Roo.reg('fckeditor', Roo.form.FCKeditor);
43057
43058 function FCKeditor_OnComplete(editorInstance){
43059     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
43060     f.fckEditor = editorInstance;
43061     //console.log("loaded");
43062     f.fireEvent('editorinit', f, editorInstance);
43063
43064   
43065
43066  
43067
43068
43069
43070
43071
43072
43073
43074
43075
43076
43077
43078
43079
43080
43081
43082 //<script type="text/javascript">
43083 /**
43084  * @class Roo.form.GridField
43085  * @extends Roo.form.Field
43086  * Embed a grid (or editable grid into a form)
43087  * STATUS ALPHA
43088  * 
43089  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
43090  * it needs 
43091  * xgrid.store = Roo.data.Store
43092  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
43093  * xgrid.store.reader = Roo.data.JsonReader 
43094  * 
43095  * 
43096  * @constructor
43097  * Creates a new GridField
43098  * @param {Object} config Configuration options
43099  */
43100 Roo.form.GridField = function(config){
43101     Roo.form.GridField.superclass.constructor.call(this, config);
43102      
43103 };
43104
43105 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
43106     /**
43107      * @cfg {Number} width  - used to restrict width of grid..
43108      */
43109     width : 100,
43110     /**
43111      * @cfg {Number} height - used to restrict height of grid..
43112      */
43113     height : 50,
43114      /**
43115      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
43116          * 
43117          *}
43118      */
43119     xgrid : false, 
43120     /**
43121      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
43122      * {tag: "input", type: "checkbox", autocomplete: "off"})
43123      */
43124    // defaultAutoCreate : { tag: 'div' },
43125     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
43126     /**
43127      * @cfg {String} addTitle Text to include for adding a title.
43128      */
43129     addTitle : false,
43130     //
43131     onResize : function(){
43132         Roo.form.Field.superclass.onResize.apply(this, arguments);
43133     },
43134
43135     initEvents : function(){
43136         // Roo.form.Checkbox.superclass.initEvents.call(this);
43137         // has no events...
43138        
43139     },
43140
43141
43142     getResizeEl : function(){
43143         return this.wrap;
43144     },
43145
43146     getPositionEl : function(){
43147         return this.wrap;
43148     },
43149
43150     // private
43151     onRender : function(ct, position){
43152         
43153         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
43154         var style = this.style;
43155         delete this.style;
43156         
43157         Roo.form.GridField.superclass.onRender.call(this, ct, position);
43158         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
43159         this.viewEl = this.wrap.createChild({ tag: 'div' });
43160         if (style) {
43161             this.viewEl.applyStyles(style);
43162         }
43163         if (this.width) {
43164             this.viewEl.setWidth(this.width);
43165         }
43166         if (this.height) {
43167             this.viewEl.setHeight(this.height);
43168         }
43169         //if(this.inputValue !== undefined){
43170         //this.setValue(this.value);
43171         
43172         
43173         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
43174         
43175         
43176         this.grid.render();
43177         this.grid.getDataSource().on('remove', this.refreshValue, this);
43178         this.grid.getDataSource().on('update', this.refreshValue, this);
43179         this.grid.on('afteredit', this.refreshValue, this);
43180  
43181     },
43182      
43183     
43184     /**
43185      * Sets the value of the item. 
43186      * @param {String} either an object  or a string..
43187      */
43188     setValue : function(v){
43189         //this.value = v;
43190         v = v || []; // empty set..
43191         // this does not seem smart - it really only affects memoryproxy grids..
43192         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
43193             var ds = this.grid.getDataSource();
43194             // assumes a json reader..
43195             var data = {}
43196             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
43197             ds.loadData( data);
43198         }
43199         // clear selection so it does not get stale.
43200         if (this.grid.sm) { 
43201             this.grid.sm.clearSelections();
43202         }
43203         
43204         Roo.form.GridField.superclass.setValue.call(this, v);
43205         this.refreshValue();
43206         // should load data in the grid really....
43207     },
43208     
43209     // private
43210     refreshValue: function() {
43211          var val = [];
43212         this.grid.getDataSource().each(function(r) {
43213             val.push(r.data);
43214         });
43215         this.el.dom.value = Roo.encode(val);
43216     }
43217     
43218      
43219     
43220     
43221 });/*
43222  * Based on:
43223  * Ext JS Library 1.1.1
43224  * Copyright(c) 2006-2007, Ext JS, LLC.
43225  *
43226  * Originally Released Under LGPL - original licence link has changed is not relivant.
43227  *
43228  * Fork - LGPL
43229  * <script type="text/javascript">
43230  */
43231 /**
43232  * @class Roo.form.DisplayField
43233  * @extends Roo.form.Field
43234  * A generic Field to display non-editable data.
43235  * @constructor
43236  * Creates a new Display Field item.
43237  * @param {Object} config Configuration options
43238  */
43239 Roo.form.DisplayField = function(config){
43240     Roo.form.DisplayField.superclass.constructor.call(this, config);
43241     
43242 };
43243
43244 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
43245     inputType:      'hidden',
43246     allowBlank:     true,
43247     readOnly:         true,
43248     
43249  
43250     /**
43251      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
43252      */
43253     focusClass : undefined,
43254     /**
43255      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
43256      */
43257     fieldClass: 'x-form-field',
43258     
43259      /**
43260      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
43261      */
43262     valueRenderer: undefined,
43263     
43264     width: 100,
43265     /**
43266      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
43267      * {tag: "input", type: "checkbox", autocomplete: "off"})
43268      */
43269      
43270  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
43271
43272     onResize : function(){
43273         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
43274         
43275     },
43276
43277     initEvents : function(){
43278         // Roo.form.Checkbox.superclass.initEvents.call(this);
43279         // has no events...
43280        
43281     },
43282
43283
43284     getResizeEl : function(){
43285         return this.wrap;
43286     },
43287
43288     getPositionEl : function(){
43289         return this.wrap;
43290     },
43291
43292     // private
43293     onRender : function(ct, position){
43294         
43295         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
43296         //if(this.inputValue !== undefined){
43297         this.wrap = this.el.wrap();
43298         
43299         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
43300         
43301         if (this.bodyStyle) {
43302             this.viewEl.applyStyles(this.bodyStyle);
43303         }
43304         //this.viewEl.setStyle('padding', '2px');
43305         
43306         this.setValue(this.value);
43307         
43308     },
43309 /*
43310     // private
43311     initValue : Roo.emptyFn,
43312
43313   */
43314
43315         // private
43316     onClick : function(){
43317         
43318     },
43319
43320     /**
43321      * Sets the checked state of the checkbox.
43322      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
43323      */
43324     setValue : function(v){
43325         this.value = v;
43326         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
43327         // this might be called before we have a dom element..
43328         if (!this.viewEl) {
43329             return;
43330         }
43331         this.viewEl.dom.innerHTML = html;
43332         Roo.form.DisplayField.superclass.setValue.call(this, v);
43333
43334     }
43335 });/*
43336  * 
43337  * Licence- LGPL
43338  * 
43339  */
43340
43341 /**
43342  * @class Roo.form.DayPicker
43343  * @extends Roo.form.Field
43344  * A Day picker show [M] [T] [W] ....
43345  * @constructor
43346  * Creates a new Day Picker
43347  * @param {Object} config Configuration options
43348  */
43349 Roo.form.DayPicker= function(config){
43350     Roo.form.DayPicker.superclass.constructor.call(this, config);
43351      
43352 };
43353
43354 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
43355     /**
43356      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
43357      */
43358     focusClass : undefined,
43359     /**
43360      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
43361      */
43362     fieldClass: "x-form-field",
43363    
43364     /**
43365      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
43366      * {tag: "input", type: "checkbox", autocomplete: "off"})
43367      */
43368     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
43369     
43370    
43371     actionMode : 'viewEl', 
43372     //
43373     // private
43374  
43375     inputType : 'hidden',
43376     
43377      
43378     inputElement: false, // real input element?
43379     basedOn: false, // ????
43380     
43381     isFormField: true, // not sure where this is needed!!!!
43382
43383     onResize : function(){
43384         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
43385         if(!this.boxLabel){
43386             this.el.alignTo(this.wrap, 'c-c');
43387         }
43388     },
43389
43390     initEvents : function(){
43391         Roo.form.Checkbox.superclass.initEvents.call(this);
43392         this.el.on("click", this.onClick,  this);
43393         this.el.on("change", this.onClick,  this);
43394     },
43395
43396
43397     getResizeEl : function(){
43398         return this.wrap;
43399     },
43400
43401     getPositionEl : function(){
43402         return this.wrap;
43403     },
43404
43405     
43406     // private
43407     onRender : function(ct, position){
43408         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
43409        
43410         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
43411         
43412         var r1 = '<table><tr>';
43413         var r2 = '<tr class="x-form-daypick-icons">';
43414         for (var i=0; i < 7; i++) {
43415             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
43416             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
43417         }
43418         
43419         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
43420         viewEl.select('img').on('click', this.onClick, this);
43421         this.viewEl = viewEl;   
43422         
43423         
43424         // this will not work on Chrome!!!
43425         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
43426         this.el.on('propertychange', this.setFromHidden,  this);  //ie
43427         
43428         
43429           
43430
43431     },
43432
43433     // private
43434     initValue : Roo.emptyFn,
43435
43436     /**
43437      * Returns the checked state of the checkbox.
43438      * @return {Boolean} True if checked, else false
43439      */
43440     getValue : function(){
43441         return this.el.dom.value;
43442         
43443     },
43444
43445         // private
43446     onClick : function(e){ 
43447         //this.setChecked(!this.checked);
43448         Roo.get(e.target).toggleClass('x-menu-item-checked');
43449         this.refreshValue();
43450         //if(this.el.dom.checked != this.checked){
43451         //    this.setValue(this.el.dom.checked);
43452        // }
43453     },
43454     
43455     // private
43456     refreshValue : function()
43457     {
43458         var val = '';
43459         this.viewEl.select('img',true).each(function(e,i,n)  {
43460             val += e.is(".x-menu-item-checked") ? String(n) : '';
43461         });
43462         this.setValue(val, true);
43463     },
43464
43465     /**
43466      * Sets the checked state of the checkbox.
43467      * On is always based on a string comparison between inputValue and the param.
43468      * @param {Boolean/String} value - the value to set 
43469      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
43470      */
43471     setValue : function(v,suppressEvent){
43472         if (!this.el.dom) {
43473             return;
43474         }
43475         var old = this.el.dom.value ;
43476         this.el.dom.value = v;
43477         if (suppressEvent) {
43478             return ;
43479         }
43480          
43481         // update display..
43482         this.viewEl.select('img',true).each(function(e,i,n)  {
43483             
43484             var on = e.is(".x-menu-item-checked");
43485             var newv = v.indexOf(String(n)) > -1;
43486             if (on != newv) {
43487                 e.toggleClass('x-menu-item-checked');
43488             }
43489             
43490         });
43491         
43492         
43493         this.fireEvent('change', this, v, old);
43494         
43495         
43496     },
43497    
43498     // handle setting of hidden value by some other method!!?!?
43499     setFromHidden: function()
43500     {
43501         if(!this.el){
43502             return;
43503         }
43504         //console.log("SET FROM HIDDEN");
43505         //alert('setFrom hidden');
43506         this.setValue(this.el.dom.value);
43507     },
43508     
43509     onDestroy : function()
43510     {
43511         if(this.viewEl){
43512             Roo.get(this.viewEl).remove();
43513         }
43514          
43515         Roo.form.DayPicker.superclass.onDestroy.call(this);
43516     }
43517
43518 });/*
43519  * RooJS Library 1.1.1
43520  * Copyright(c) 2008-2011  Alan Knowles
43521  *
43522  * License - LGPL
43523  */
43524  
43525
43526 /**
43527  * @class Roo.form.ComboCheck
43528  * @extends Roo.form.ComboBox
43529  * A combobox for multiple select items.
43530  *
43531  * FIXME - could do with a reset button..
43532  * 
43533  * @constructor
43534  * Create a new ComboCheck
43535  * @param {Object} config Configuration options
43536  */
43537 Roo.form.ComboCheck = function(config){
43538     Roo.form.ComboCheck.superclass.constructor.call(this, config);
43539     // should verify some data...
43540     // like
43541     // hiddenName = required..
43542     // displayField = required
43543     // valudField == required
43544     var req= [ 'hiddenName', 'displayField', 'valueField' ];
43545     var _t = this;
43546     Roo.each(req, function(e) {
43547         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
43548             throw "Roo.form.ComboCheck : missing value for: " + e;
43549         }
43550     });
43551     
43552     
43553 };
43554
43555 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
43556      
43557      
43558     editable : false,
43559      
43560     selectedClass: 'x-menu-item-checked', 
43561     
43562     // private
43563     onRender : function(ct, position){
43564         var _t = this;
43565         
43566         
43567         
43568         if(!this.tpl){
43569             var cls = 'x-combo-list';
43570
43571             
43572             this.tpl =  new Roo.Template({
43573                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
43574                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
43575                    '<span>{' + this.displayField + '}</span>' +
43576                     '</div>' 
43577                 
43578             });
43579         }
43580  
43581         
43582         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
43583         this.view.singleSelect = false;
43584         this.view.multiSelect = true;
43585         this.view.toggleSelect = true;
43586         this.pageTb.add(new Roo.Toolbar.Fill(), {
43587             
43588             text: 'Done',
43589             handler: function()
43590             {
43591                 _t.collapse();
43592             }
43593         });
43594     },
43595     
43596     onViewOver : function(e, t){
43597         // do nothing...
43598         return;
43599         
43600     },
43601     
43602     onViewClick : function(doFocus,index){
43603         return;
43604         
43605     },
43606     select: function () {
43607         //Roo.log("SELECT CALLED");
43608     },
43609      
43610     selectByValue : function(xv, scrollIntoView){
43611         var ar = this.getValueArray();
43612         var sels = [];
43613         
43614         Roo.each(ar, function(v) {
43615             if(v === undefined || v === null){
43616                 return;
43617             }
43618             var r = this.findRecord(this.valueField, v);
43619             if(r){
43620                 sels.push(this.store.indexOf(r))
43621                 
43622             }
43623         },this);
43624         this.view.select(sels);
43625         return false;
43626     },
43627     
43628     
43629     
43630     onSelect : function(record, index){
43631        // Roo.log("onselect Called");
43632        // this is only called by the clear button now..
43633         this.view.clearSelections();
43634         this.setValue('[]');
43635         if (this.value != this.valueBefore) {
43636             this.fireEvent('change', this, this.value, this.valueBefore);
43637         }
43638     },
43639     getValueArray : function()
43640     {
43641         var ar = [] ;
43642         
43643         try {
43644             Roo.log(this.value);
43645             var ar = Roo.decode(this.value);
43646             return  ar instanceof Array ? ar : []; //?? valid?
43647             
43648         } catch(e) {
43649             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
43650             return [];
43651         }
43652          
43653     },
43654     expand : function ()
43655     {
43656         Roo.form.ComboCheck.superclass.expand.call(this);
43657         this.valueBefore = this.value;
43658         
43659
43660     },
43661     
43662     collapse : function(){
43663         Roo.form.ComboCheck.superclass.collapse.call(this);
43664         var sl = this.view.getSelectedIndexes();
43665         var st = this.store;
43666         var nv = [];
43667         var tv = [];
43668         var r;
43669         Roo.each(sl, function(i) {
43670             r = st.getAt(i);
43671             nv.push(r.get(this.valueField));
43672         },this);
43673         this.setValue(Roo.encode(nv));
43674         if (this.value != this.valueBefore) {
43675
43676             this.fireEvent('change', this, this.value, this.valueBefore);
43677         }
43678         
43679     },
43680     
43681     setValue : function(v){
43682         // Roo.log(v);
43683         this.value = v;
43684         
43685         var vals = this.getValueArray();
43686         var tv = [];
43687         Roo.each(vals, function(k) {
43688             var r = this.findRecord(this.valueField, k);
43689             if(r){
43690                 tv.push(r.data[this.displayField]);
43691             }else if(this.valueNotFoundText !== undefined){
43692                 tv.push( this.valueNotFoundText );
43693             }
43694         },this);
43695        // Roo.log(tv);
43696         
43697         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
43698         this.hiddenField.value = v;
43699         this.value = v;
43700     }
43701     
43702 });//<script type="text/javasscript">
43703  
43704
43705 /**
43706  * @class Roo.DDView
43707  * A DnD enabled version of Roo.View.
43708  * @param {Element/String} container The Element in which to create the View.
43709  * @param {String} tpl The template string used to create the markup for each element of the View
43710  * @param {Object} config The configuration properties. These include all the config options of
43711  * {@link Roo.View} plus some specific to this class.<br>
43712  * <p>
43713  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
43714  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
43715  * <p>
43716  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
43717 .x-view-drag-insert-above {
43718         border-top:1px dotted #3366cc;
43719 }
43720 .x-view-drag-insert-below {
43721         border-bottom:1px dotted #3366cc;
43722 }
43723 </code></pre>
43724  * 
43725  */
43726  
43727 Roo.DDView = function(container, tpl, config) {
43728     Roo.DDView.superclass.constructor.apply(this, arguments);
43729     this.getEl().setStyle("outline", "0px none");
43730     this.getEl().unselectable();
43731     if (this.dragGroup) {
43732                 this.setDraggable(this.dragGroup.split(","));
43733     }
43734     if (this.dropGroup) {
43735                 this.setDroppable(this.dropGroup.split(","));
43736     }
43737     if (this.deletable) {
43738         this.setDeletable();
43739     }
43740     this.isDirtyFlag = false;
43741         this.addEvents({
43742                 "drop" : true
43743         });
43744 };
43745
43746 Roo.extend(Roo.DDView, Roo.View, {
43747 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
43748 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
43749 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
43750 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
43751
43752         isFormField: true,
43753
43754         reset: Roo.emptyFn,
43755         
43756         clearInvalid: Roo.form.Field.prototype.clearInvalid,
43757
43758         validate: function() {
43759                 return true;
43760         },
43761         
43762         destroy: function() {
43763                 this.purgeListeners();
43764                 this.getEl.removeAllListeners();
43765                 this.getEl().remove();
43766                 if (this.dragZone) {
43767                         if (this.dragZone.destroy) {
43768                                 this.dragZone.destroy();
43769                         }
43770                 }
43771                 if (this.dropZone) {
43772                         if (this.dropZone.destroy) {
43773                                 this.dropZone.destroy();
43774                         }
43775                 }
43776         },
43777
43778 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
43779         getName: function() {
43780                 return this.name;
43781         },
43782
43783 /**     Loads the View from a JSON string representing the Records to put into the Store. */
43784         setValue: function(v) {
43785                 if (!this.store) {
43786                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
43787                 }
43788                 var data = {};
43789                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
43790                 this.store.proxy = new Roo.data.MemoryProxy(data);
43791                 this.store.load();
43792         },
43793
43794 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
43795         getValue: function() {
43796                 var result = '(';
43797                 this.store.each(function(rec) {
43798                         result += rec.id + ',';
43799                 });
43800                 return result.substr(0, result.length - 1) + ')';
43801         },
43802         
43803         getIds: function() {
43804                 var i = 0, result = new Array(this.store.getCount());
43805                 this.store.each(function(rec) {
43806                         result[i++] = rec.id;
43807                 });
43808                 return result;
43809         },
43810         
43811         isDirty: function() {
43812                 return this.isDirtyFlag;
43813         },
43814
43815 /**
43816  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
43817  *      whole Element becomes the target, and this causes the drop gesture to append.
43818  */
43819     getTargetFromEvent : function(e) {
43820                 var target = e.getTarget();
43821                 while ((target !== null) && (target.parentNode != this.el.dom)) {
43822                 target = target.parentNode;
43823                 }
43824                 if (!target) {
43825                         target = this.el.dom.lastChild || this.el.dom;
43826                 }
43827                 return target;
43828     },
43829
43830 /**
43831  *      Create the drag data which consists of an object which has the property "ddel" as
43832  *      the drag proxy element. 
43833  */
43834     getDragData : function(e) {
43835         var target = this.findItemFromChild(e.getTarget());
43836                 if(target) {
43837                         this.handleSelection(e);
43838                         var selNodes = this.getSelectedNodes();
43839             var dragData = {
43840                 source: this,
43841                 copy: this.copy || (this.allowCopy && e.ctrlKey),
43842                 nodes: selNodes,
43843                 records: []
43844                         };
43845                         var selectedIndices = this.getSelectedIndexes();
43846                         for (var i = 0; i < selectedIndices.length; i++) {
43847                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
43848                         }
43849                         if (selNodes.length == 1) {
43850                                 dragData.ddel = target.cloneNode(true); // the div element
43851                         } else {
43852                                 var div = document.createElement('div'); // create the multi element drag "ghost"
43853                                 div.className = 'multi-proxy';
43854                                 for (var i = 0, len = selNodes.length; i < len; i++) {
43855                                         div.appendChild(selNodes[i].cloneNode(true));
43856                                 }
43857                                 dragData.ddel = div;
43858                         }
43859             //console.log(dragData)
43860             //console.log(dragData.ddel.innerHTML)
43861                         return dragData;
43862                 }
43863         //console.log('nodragData')
43864                 return false;
43865     },
43866     
43867 /**     Specify to which ddGroup items in this DDView may be dragged. */
43868     setDraggable: function(ddGroup) {
43869         if (ddGroup instanceof Array) {
43870                 Roo.each(ddGroup, this.setDraggable, this);
43871                 return;
43872         }
43873         if (this.dragZone) {
43874                 this.dragZone.addToGroup(ddGroup);
43875         } else {
43876                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
43877                                 containerScroll: true,
43878                                 ddGroup: ddGroup 
43879
43880                         });
43881 //                      Draggability implies selection. DragZone's mousedown selects the element.
43882                         if (!this.multiSelect) { this.singleSelect = true; }
43883
43884 //                      Wire the DragZone's handlers up to methods in *this*
43885                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
43886                 }
43887     },
43888
43889 /**     Specify from which ddGroup this DDView accepts drops. */
43890     setDroppable: function(ddGroup) {
43891         if (ddGroup instanceof Array) {
43892                 Roo.each(ddGroup, this.setDroppable, this);
43893                 return;
43894         }
43895         if (this.dropZone) {
43896                 this.dropZone.addToGroup(ddGroup);
43897         } else {
43898                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
43899                                 containerScroll: true,
43900                                 ddGroup: ddGroup
43901                         });
43902
43903 //                      Wire the DropZone's handlers up to methods in *this*
43904                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
43905                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
43906                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
43907                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
43908                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
43909                 }
43910     },
43911
43912 /**     Decide whether to drop above or below a View node. */
43913     getDropPoint : function(e, n, dd){
43914         if (n == this.el.dom) { return "above"; }
43915                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
43916                 var c = t + (b - t) / 2;
43917                 var y = Roo.lib.Event.getPageY(e);
43918                 if(y <= c) {
43919                         return "above";
43920                 }else{
43921                         return "below";
43922                 }
43923     },
43924
43925     onNodeEnter : function(n, dd, e, data){
43926                 return false;
43927     },
43928     
43929     onNodeOver : function(n, dd, e, data){
43930                 var pt = this.getDropPoint(e, n, dd);
43931                 // set the insert point style on the target node
43932                 var dragElClass = this.dropNotAllowed;
43933                 if (pt) {
43934                         var targetElClass;
43935                         if (pt == "above"){
43936                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
43937                                 targetElClass = "x-view-drag-insert-above";
43938                         } else {
43939                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
43940                                 targetElClass = "x-view-drag-insert-below";
43941                         }
43942                         if (this.lastInsertClass != targetElClass){
43943                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
43944                                 this.lastInsertClass = targetElClass;
43945                         }
43946                 }
43947                 return dragElClass;
43948         },
43949
43950     onNodeOut : function(n, dd, e, data){
43951                 this.removeDropIndicators(n);
43952     },
43953
43954     onNodeDrop : function(n, dd, e, data){
43955         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
43956                 return false;
43957         }
43958         var pt = this.getDropPoint(e, n, dd);
43959                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
43960                 if (pt == "below") { insertAt++; }
43961                 for (var i = 0; i < data.records.length; i++) {
43962                         var r = data.records[i];
43963                         var dup = this.store.getById(r.id);
43964                         if (dup && (dd != this.dragZone)) {
43965                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
43966                         } else {
43967                                 if (data.copy) {
43968                                         this.store.insert(insertAt++, r.copy());
43969                                 } else {
43970                                         data.source.isDirtyFlag = true;
43971                                         r.store.remove(r);
43972                                         this.store.insert(insertAt++, r);
43973                                 }
43974                                 this.isDirtyFlag = true;
43975                         }
43976                 }
43977                 this.dragZone.cachedTarget = null;
43978                 return true;
43979     },
43980
43981     removeDropIndicators : function(n){
43982                 if(n){
43983                         Roo.fly(n).removeClass([
43984                                 "x-view-drag-insert-above",
43985                                 "x-view-drag-insert-below"]);
43986                         this.lastInsertClass = "_noclass";
43987                 }
43988     },
43989
43990 /**
43991  *      Utility method. Add a delete option to the DDView's context menu.
43992  *      @param {String} imageUrl The URL of the "delete" icon image.
43993  */
43994         setDeletable: function(imageUrl) {
43995                 if (!this.singleSelect && !this.multiSelect) {
43996                         this.singleSelect = true;
43997                 }
43998                 var c = this.getContextMenu();
43999                 this.contextMenu.on("itemclick", function(item) {
44000                         switch (item.id) {
44001                                 case "delete":
44002                                         this.remove(this.getSelectedIndexes());
44003                                         break;
44004                         }
44005                 }, this);
44006                 this.contextMenu.add({
44007                         icon: imageUrl,
44008                         id: "delete",
44009                         text: 'Delete'
44010                 });
44011         },
44012         
44013 /**     Return the context menu for this DDView. */
44014         getContextMenu: function() {
44015                 if (!this.contextMenu) {
44016 //                      Create the View's context menu
44017                         this.contextMenu = new Roo.menu.Menu({
44018                                 id: this.id + "-contextmenu"
44019                         });
44020                         this.el.on("contextmenu", this.showContextMenu, this);
44021                 }
44022                 return this.contextMenu;
44023         },
44024         
44025         disableContextMenu: function() {
44026                 if (this.contextMenu) {
44027                         this.el.un("contextmenu", this.showContextMenu, this);
44028                 }
44029         },
44030
44031         showContextMenu: function(e, item) {
44032         item = this.findItemFromChild(e.getTarget());
44033                 if (item) {
44034                         e.stopEvent();
44035                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
44036                         this.contextMenu.showAt(e.getXY());
44037             }
44038     },
44039
44040 /**
44041  *      Remove {@link Roo.data.Record}s at the specified indices.
44042  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
44043  */
44044     remove: function(selectedIndices) {
44045                 selectedIndices = [].concat(selectedIndices);
44046                 for (var i = 0; i < selectedIndices.length; i++) {
44047                         var rec = this.store.getAt(selectedIndices[i]);
44048                         this.store.remove(rec);
44049                 }
44050     },
44051
44052 /**
44053  *      Double click fires the event, but also, if this is draggable, and there is only one other
44054  *      related DropZone, it transfers the selected node.
44055  */
44056     onDblClick : function(e){
44057         var item = this.findItemFromChild(e.getTarget());
44058         if(item){
44059             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
44060                 return false;
44061             }
44062             if (this.dragGroup) {
44063                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
44064                     while (targets.indexOf(this.dropZone) > -1) {
44065                             targets.remove(this.dropZone);
44066                                 }
44067                     if (targets.length == 1) {
44068                                         this.dragZone.cachedTarget = null;
44069                         var el = Roo.get(targets[0].getEl());
44070                         var box = el.getBox(true);
44071                         targets[0].onNodeDrop(el.dom, {
44072                                 target: el.dom,
44073                                 xy: [box.x, box.y + box.height - 1]
44074                         }, null, this.getDragData(e));
44075                     }
44076                 }
44077         }
44078     },
44079     
44080     handleSelection: function(e) {
44081                 this.dragZone.cachedTarget = null;
44082         var item = this.findItemFromChild(e.getTarget());
44083         if (!item) {
44084                 this.clearSelections(true);
44085                 return;
44086         }
44087                 if (item && (this.multiSelect || this.singleSelect)){
44088                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
44089                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
44090                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
44091                                 this.unselect(item);
44092                         } else {
44093                                 this.select(item, this.multiSelect && e.ctrlKey);
44094                                 this.lastSelection = item;
44095                         }
44096                 }
44097     },
44098
44099     onItemClick : function(item, index, e){
44100                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
44101                         return false;
44102                 }
44103                 return true;
44104     },
44105
44106     unselect : function(nodeInfo, suppressEvent){
44107                 var node = this.getNode(nodeInfo);
44108                 if(node && this.isSelected(node)){
44109                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
44110                                 Roo.fly(node).removeClass(this.selectedClass);
44111                                 this.selections.remove(node);
44112                                 if(!suppressEvent){
44113                                         this.fireEvent("selectionchange", this, this.selections);
44114                                 }
44115                         }
44116                 }
44117     }
44118 });
44119 /*
44120  * Based on:
44121  * Ext JS Library 1.1.1
44122  * Copyright(c) 2006-2007, Ext JS, LLC.
44123  *
44124  * Originally Released Under LGPL - original licence link has changed is not relivant.
44125  *
44126  * Fork - LGPL
44127  * <script type="text/javascript">
44128  */
44129  
44130 /**
44131  * @class Roo.LayoutManager
44132  * @extends Roo.util.Observable
44133  * Base class for layout managers.
44134  */
44135 Roo.LayoutManager = function(container, config){
44136     Roo.LayoutManager.superclass.constructor.call(this);
44137     this.el = Roo.get(container);
44138     // ie scrollbar fix
44139     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
44140         document.body.scroll = "no";
44141     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
44142         this.el.position('relative');
44143     }
44144     this.id = this.el.id;
44145     this.el.addClass("x-layout-container");
44146     /** false to disable window resize monitoring @type Boolean */
44147     this.monitorWindowResize = true;
44148     this.regions = {};
44149     this.addEvents({
44150         /**
44151          * @event layout
44152          * Fires when a layout is performed. 
44153          * @param {Roo.LayoutManager} this
44154          */
44155         "layout" : true,
44156         /**
44157          * @event regionresized
44158          * Fires when the user resizes a region. 
44159          * @param {Roo.LayoutRegion} region The resized region
44160          * @param {Number} newSize The new size (width for east/west, height for north/south)
44161          */
44162         "regionresized" : true,
44163         /**
44164          * @event regioncollapsed
44165          * Fires when a region is collapsed. 
44166          * @param {Roo.LayoutRegion} region The collapsed region
44167          */
44168         "regioncollapsed" : true,
44169         /**
44170          * @event regionexpanded
44171          * Fires when a region is expanded.  
44172          * @param {Roo.LayoutRegion} region The expanded region
44173          */
44174         "regionexpanded" : true
44175     });
44176     this.updating = false;
44177     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
44178 };
44179
44180 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
44181     /**
44182      * Returns true if this layout is currently being updated
44183      * @return {Boolean}
44184      */
44185     isUpdating : function(){
44186         return this.updating; 
44187     },
44188     
44189     /**
44190      * Suspend the LayoutManager from doing auto-layouts while
44191      * making multiple add or remove calls
44192      */
44193     beginUpdate : function(){
44194         this.updating = true;    
44195     },
44196     
44197     /**
44198      * Restore auto-layouts and optionally disable the manager from performing a layout
44199      * @param {Boolean} noLayout true to disable a layout update 
44200      */
44201     endUpdate : function(noLayout){
44202         this.updating = false;
44203         if(!noLayout){
44204             this.layout();
44205         }    
44206     },
44207     
44208     layout: function(){
44209         
44210     },
44211     
44212     onRegionResized : function(region, newSize){
44213         this.fireEvent("regionresized", region, newSize);
44214         this.layout();
44215     },
44216     
44217     onRegionCollapsed : function(region){
44218         this.fireEvent("regioncollapsed", region);
44219     },
44220     
44221     onRegionExpanded : function(region){
44222         this.fireEvent("regionexpanded", region);
44223     },
44224         
44225     /**
44226      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
44227      * performs box-model adjustments.
44228      * @return {Object} The size as an object {width: (the width), height: (the height)}
44229      */
44230     getViewSize : function(){
44231         var size;
44232         if(this.el.dom != document.body){
44233             size = this.el.getSize();
44234         }else{
44235             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
44236         }
44237         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
44238         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
44239         return size;
44240     },
44241     
44242     /**
44243      * Returns the Element this layout is bound to.
44244      * @return {Roo.Element}
44245      */
44246     getEl : function(){
44247         return this.el;
44248     },
44249     
44250     /**
44251      * Returns the specified region.
44252      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
44253      * @return {Roo.LayoutRegion}
44254      */
44255     getRegion : function(target){
44256         return this.regions[target.toLowerCase()];
44257     },
44258     
44259     onWindowResize : function(){
44260         if(this.monitorWindowResize){
44261             this.layout();
44262         }
44263     }
44264 });/*
44265  * Based on:
44266  * Ext JS Library 1.1.1
44267  * Copyright(c) 2006-2007, Ext JS, LLC.
44268  *
44269  * Originally Released Under LGPL - original licence link has changed is not relivant.
44270  *
44271  * Fork - LGPL
44272  * <script type="text/javascript">
44273  */
44274 /**
44275  * @class Roo.BorderLayout
44276  * @extends Roo.LayoutManager
44277  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
44278  * please see: <br><br>
44279  * <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>
44280  * <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>
44281  * Example:
44282  <pre><code>
44283  var layout = new Roo.BorderLayout(document.body, {
44284     north: {
44285         initialSize: 25,
44286         titlebar: false
44287     },
44288     west: {
44289         split:true,
44290         initialSize: 200,
44291         minSize: 175,
44292         maxSize: 400,
44293         titlebar: true,
44294         collapsible: true
44295     },
44296     east: {
44297         split:true,
44298         initialSize: 202,
44299         minSize: 175,
44300         maxSize: 400,
44301         titlebar: true,
44302         collapsible: true
44303     },
44304     south: {
44305         split:true,
44306         initialSize: 100,
44307         minSize: 100,
44308         maxSize: 200,
44309         titlebar: true,
44310         collapsible: true
44311     },
44312     center: {
44313         titlebar: true,
44314         autoScroll:true,
44315         resizeTabs: true,
44316         minTabWidth: 50,
44317         preferredTabWidth: 150
44318     }
44319 });
44320
44321 // shorthand
44322 var CP = Roo.ContentPanel;
44323
44324 layout.beginUpdate();
44325 layout.add("north", new CP("north", "North"));
44326 layout.add("south", new CP("south", {title: "South", closable: true}));
44327 layout.add("west", new CP("west", {title: "West"}));
44328 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
44329 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
44330 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
44331 layout.getRegion("center").showPanel("center1");
44332 layout.endUpdate();
44333 </code></pre>
44334
44335 <b>The container the layout is rendered into can be either the body element or any other element.
44336 If it is not the body element, the container needs to either be an absolute positioned element,
44337 or you will need to add "position:relative" to the css of the container.  You will also need to specify
44338 the container size if it is not the body element.</b>
44339
44340 * @constructor
44341 * Create a new BorderLayout
44342 * @param {String/HTMLElement/Element} container The container this layout is bound to
44343 * @param {Object} config Configuration options
44344  */
44345 Roo.BorderLayout = function(container, config){
44346     config = config || {};
44347     Roo.BorderLayout.superclass.constructor.call(this, container, config);
44348     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
44349     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
44350         var target = this.factory.validRegions[i];
44351         if(config[target]){
44352             this.addRegion(target, config[target]);
44353         }
44354     }
44355 };
44356
44357 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
44358     /**
44359      * Creates and adds a new region if it doesn't already exist.
44360      * @param {String} target The target region key (north, south, east, west or center).
44361      * @param {Object} config The regions config object
44362      * @return {BorderLayoutRegion} The new region
44363      */
44364     addRegion : function(target, config){
44365         if(!this.regions[target]){
44366             var r = this.factory.create(target, this, config);
44367             this.bindRegion(target, r);
44368         }
44369         return this.regions[target];
44370     },
44371
44372     // private (kinda)
44373     bindRegion : function(name, r){
44374         this.regions[name] = r;
44375         r.on("visibilitychange", this.layout, this);
44376         r.on("paneladded", this.layout, this);
44377         r.on("panelremoved", this.layout, this);
44378         r.on("invalidated", this.layout, this);
44379         r.on("resized", this.onRegionResized, this);
44380         r.on("collapsed", this.onRegionCollapsed, this);
44381         r.on("expanded", this.onRegionExpanded, this);
44382     },
44383
44384     /**
44385      * Performs a layout update.
44386      */
44387     layout : function(){
44388         if(this.updating) return;
44389         var size = this.getViewSize();
44390         var w = size.width;
44391         var h = size.height;
44392         var centerW = w;
44393         var centerH = h;
44394         var centerY = 0;
44395         var centerX = 0;
44396         //var x = 0, y = 0;
44397
44398         var rs = this.regions;
44399         var north = rs["north"];
44400         var south = rs["south"]; 
44401         var west = rs["west"];
44402         var east = rs["east"];
44403         var center = rs["center"];
44404         //if(this.hideOnLayout){ // not supported anymore
44405             //c.el.setStyle("display", "none");
44406         //}
44407         if(north && north.isVisible()){
44408             var b = north.getBox();
44409             var m = north.getMargins();
44410             b.width = w - (m.left+m.right);
44411             b.x = m.left;
44412             b.y = m.top;
44413             centerY = b.height + b.y + m.bottom;
44414             centerH -= centerY;
44415             north.updateBox(this.safeBox(b));
44416         }
44417         if(south && south.isVisible()){
44418             var b = south.getBox();
44419             var m = south.getMargins();
44420             b.width = w - (m.left+m.right);
44421             b.x = m.left;
44422             var totalHeight = (b.height + m.top + m.bottom);
44423             b.y = h - totalHeight + m.top;
44424             centerH -= totalHeight;
44425             south.updateBox(this.safeBox(b));
44426         }
44427         if(west && west.isVisible()){
44428             var b = west.getBox();
44429             var m = west.getMargins();
44430             b.height = centerH - (m.top+m.bottom);
44431             b.x = m.left;
44432             b.y = centerY + m.top;
44433             var totalWidth = (b.width + m.left + m.right);
44434             centerX += totalWidth;
44435             centerW -= totalWidth;
44436             west.updateBox(this.safeBox(b));
44437         }
44438         if(east && east.isVisible()){
44439             var b = east.getBox();
44440             var m = east.getMargins();
44441             b.height = centerH - (m.top+m.bottom);
44442             var totalWidth = (b.width + m.left + m.right);
44443             b.x = w - totalWidth + m.left;
44444             b.y = centerY + m.top;
44445             centerW -= totalWidth;
44446             east.updateBox(this.safeBox(b));
44447         }
44448         if(center){
44449             var m = center.getMargins();
44450             var centerBox = {
44451                 x: centerX + m.left,
44452                 y: centerY + m.top,
44453                 width: centerW - (m.left+m.right),
44454                 height: centerH - (m.top+m.bottom)
44455             };
44456             //if(this.hideOnLayout){
44457                 //center.el.setStyle("display", "block");
44458             //}
44459             center.updateBox(this.safeBox(centerBox));
44460         }
44461         this.el.repaint();
44462         this.fireEvent("layout", this);
44463     },
44464
44465     // private
44466     safeBox : function(box){
44467         box.width = Math.max(0, box.width);
44468         box.height = Math.max(0, box.height);
44469         return box;
44470     },
44471
44472     /**
44473      * Adds a ContentPanel (or subclass) to this layout.
44474      * @param {String} target The target region key (north, south, east, west or center).
44475      * @param {Roo.ContentPanel} panel The panel to add
44476      * @return {Roo.ContentPanel} The added panel
44477      */
44478     add : function(target, panel){
44479          
44480         target = target.toLowerCase();
44481         return this.regions[target].add(panel);
44482     },
44483
44484     /**
44485      * Remove a ContentPanel (or subclass) to this layout.
44486      * @param {String} target The target region key (north, south, east, west or center).
44487      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
44488      * @return {Roo.ContentPanel} The removed panel
44489      */
44490     remove : function(target, panel){
44491         target = target.toLowerCase();
44492         return this.regions[target].remove(panel);
44493     },
44494
44495     /**
44496      * Searches all regions for a panel with the specified id
44497      * @param {String} panelId
44498      * @return {Roo.ContentPanel} The panel or null if it wasn't found
44499      */
44500     findPanel : function(panelId){
44501         var rs = this.regions;
44502         for(var target in rs){
44503             if(typeof rs[target] != "function"){
44504                 var p = rs[target].getPanel(panelId);
44505                 if(p){
44506                     return p;
44507                 }
44508             }
44509         }
44510         return null;
44511     },
44512
44513     /**
44514      * Searches all regions for a panel with the specified id and activates (shows) it.
44515      * @param {String/ContentPanel} panelId The panels id or the panel itself
44516      * @return {Roo.ContentPanel} The shown panel or null
44517      */
44518     showPanel : function(panelId) {
44519       var rs = this.regions;
44520       for(var target in rs){
44521          var r = rs[target];
44522          if(typeof r != "function"){
44523             if(r.hasPanel(panelId)){
44524                return r.showPanel(panelId);
44525             }
44526          }
44527       }
44528       return null;
44529    },
44530
44531    /**
44532      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
44533      * @param {Roo.state.Provider} provider (optional) An alternate state provider
44534      */
44535     restoreState : function(provider){
44536         if(!provider){
44537             provider = Roo.state.Manager;
44538         }
44539         var sm = new Roo.LayoutStateManager();
44540         sm.init(this, provider);
44541     },
44542
44543     /**
44544      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
44545      * object should contain properties for each region to add ContentPanels to, and each property's value should be
44546      * a valid ContentPanel config object.  Example:
44547      * <pre><code>
44548 // Create the main layout
44549 var layout = new Roo.BorderLayout('main-ct', {
44550     west: {
44551         split:true,
44552         minSize: 175,
44553         titlebar: true
44554     },
44555     center: {
44556         title:'Components'
44557     }
44558 }, 'main-ct');
44559
44560 // Create and add multiple ContentPanels at once via configs
44561 layout.batchAdd({
44562    west: {
44563        id: 'source-files',
44564        autoCreate:true,
44565        title:'Ext Source Files',
44566        autoScroll:true,
44567        fitToFrame:true
44568    },
44569    center : {
44570        el: cview,
44571        autoScroll:true,
44572        fitToFrame:true,
44573        toolbar: tb,
44574        resizeEl:'cbody'
44575    }
44576 });
44577 </code></pre>
44578      * @param {Object} regions An object containing ContentPanel configs by region name
44579      */
44580     batchAdd : function(regions){
44581         this.beginUpdate();
44582         for(var rname in regions){
44583             var lr = this.regions[rname];
44584             if(lr){
44585                 this.addTypedPanels(lr, regions[rname]);
44586             }
44587         }
44588         this.endUpdate();
44589     },
44590
44591     // private
44592     addTypedPanels : function(lr, ps){
44593         if(typeof ps == 'string'){
44594             lr.add(new Roo.ContentPanel(ps));
44595         }
44596         else if(ps instanceof Array){
44597             for(var i =0, len = ps.length; i < len; i++){
44598                 this.addTypedPanels(lr, ps[i]);
44599             }
44600         }
44601         else if(!ps.events){ // raw config?
44602             var el = ps.el;
44603             delete ps.el; // prevent conflict
44604             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
44605         }
44606         else {  // panel object assumed!
44607             lr.add(ps);
44608         }
44609     },
44610     /**
44611      * Adds a xtype elements to the layout.
44612      * <pre><code>
44613
44614 layout.addxtype({
44615        xtype : 'ContentPanel',
44616        region: 'west',
44617        items: [ .... ]
44618    }
44619 );
44620
44621 layout.addxtype({
44622         xtype : 'NestedLayoutPanel',
44623         region: 'west',
44624         layout: {
44625            center: { },
44626            west: { }   
44627         },
44628         items : [ ... list of content panels or nested layout panels.. ]
44629    }
44630 );
44631 </code></pre>
44632      * @param {Object} cfg Xtype definition of item to add.
44633      */
44634     addxtype : function(cfg)
44635     {
44636         // basically accepts a pannel...
44637         // can accept a layout region..!?!?
44638         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
44639         
44640         if (!cfg.xtype.match(/Panel$/)) {
44641             return false;
44642         }
44643         var ret = false;
44644         var region = cfg.region;
44645         delete cfg.region;
44646         
44647           
44648         var xitems = [];
44649         if (cfg.items) {
44650             xitems = cfg.items;
44651             delete cfg.items;
44652         }
44653         
44654         
44655         switch(cfg.xtype) 
44656         {
44657             case 'ContentPanel':  // ContentPanel (el, cfg)
44658             case 'ScrollPanel':  // ContentPanel (el, cfg)
44659                 if(cfg.autoCreate) {
44660                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
44661                 } else {
44662                     var el = this.el.createChild();
44663                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
44664                 }
44665                 
44666                 this.add(region, ret);
44667                 break;
44668             
44669             
44670             case 'TreePanel': // our new panel!
44671                 cfg.el = this.el.createChild();
44672                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
44673                 this.add(region, ret);
44674                 break;
44675             
44676             case 'NestedLayoutPanel': 
44677                 // create a new Layout (which is  a Border Layout...
44678                 var el = this.el.createChild();
44679                 var clayout = cfg.layout;
44680                 delete cfg.layout;
44681                 clayout.items   = clayout.items  || [];
44682                 // replace this exitems with the clayout ones..
44683                 xitems = clayout.items;
44684                  
44685                 
44686                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
44687                     cfg.background = false;
44688                 }
44689                 var layout = new Roo.BorderLayout(el, clayout);
44690                 
44691                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
44692                 //console.log('adding nested layout panel '  + cfg.toSource());
44693                 this.add(region, ret);
44694                 
44695                 break;
44696                 
44697             case 'GridPanel': 
44698             
44699                 // needs grid and region
44700                 
44701                 //var el = this.getRegion(region).el.createChild();
44702                 var el = this.el.createChild();
44703                 // create the grid first...
44704                 
44705                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
44706                 delete cfg.grid;
44707                 if (region == 'center' && this.active ) {
44708                     cfg.background = false;
44709                 }
44710                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
44711                 
44712                 this.add(region, ret);
44713                 if (cfg.background) {
44714                     ret.on('activate', function(gp) {
44715                         if (!gp.grid.rendered) {
44716                             gp.grid.render();
44717                         }
44718                     });
44719                 } else {
44720                     grid.render();
44721                 }
44722                 break;
44723            
44724                
44725                 
44726                 
44727             default: 
44728                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
44729                 return null;
44730              // GridPanel (grid, cfg)
44731             
44732         }
44733         this.beginUpdate();
44734         // add children..
44735         Roo.each(xitems, function(i)  {
44736             ret.addxtype(i);
44737         });
44738         this.endUpdate();
44739         return ret;
44740         
44741     }
44742 });
44743
44744 /**
44745  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
44746  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
44747  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
44748  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
44749  * <pre><code>
44750 // shorthand
44751 var CP = Roo.ContentPanel;
44752
44753 var layout = Roo.BorderLayout.create({
44754     north: {
44755         initialSize: 25,
44756         titlebar: false,
44757         panels: [new CP("north", "North")]
44758     },
44759     west: {
44760         split:true,
44761         initialSize: 200,
44762         minSize: 175,
44763         maxSize: 400,
44764         titlebar: true,
44765         collapsible: true,
44766         panels: [new CP("west", {title: "West"})]
44767     },
44768     east: {
44769         split:true,
44770         initialSize: 202,
44771         minSize: 175,
44772         maxSize: 400,
44773         titlebar: true,
44774         collapsible: true,
44775         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
44776     },
44777     south: {
44778         split:true,
44779         initialSize: 100,
44780         minSize: 100,
44781         maxSize: 200,
44782         titlebar: true,
44783         collapsible: true,
44784         panels: [new CP("south", {title: "South", closable: true})]
44785     },
44786     center: {
44787         titlebar: true,
44788         autoScroll:true,
44789         resizeTabs: true,
44790         minTabWidth: 50,
44791         preferredTabWidth: 150,
44792         panels: [
44793             new CP("center1", {title: "Close Me", closable: true}),
44794             new CP("center2", {title: "Center Panel", closable: false})
44795         ]
44796     }
44797 }, document.body);
44798
44799 layout.getRegion("center").showPanel("center1");
44800 </code></pre>
44801  * @param config
44802  * @param targetEl
44803  */
44804 Roo.BorderLayout.create = function(config, targetEl){
44805     var layout = new Roo.BorderLayout(targetEl || document.body, config);
44806     layout.beginUpdate();
44807     var regions = Roo.BorderLayout.RegionFactory.validRegions;
44808     for(var j = 0, jlen = regions.length; j < jlen; j++){
44809         var lr = regions[j];
44810         if(layout.regions[lr] && config[lr].panels){
44811             var r = layout.regions[lr];
44812             var ps = config[lr].panels;
44813             layout.addTypedPanels(r, ps);
44814         }
44815     }
44816     layout.endUpdate();
44817     return layout;
44818 };
44819
44820 // private
44821 Roo.BorderLayout.RegionFactory = {
44822     // private
44823     validRegions : ["north","south","east","west","center"],
44824
44825     // private
44826     create : function(target, mgr, config){
44827         target = target.toLowerCase();
44828         if(config.lightweight || config.basic){
44829             return new Roo.BasicLayoutRegion(mgr, config, target);
44830         }
44831         switch(target){
44832             case "north":
44833                 return new Roo.NorthLayoutRegion(mgr, config);
44834             case "south":
44835                 return new Roo.SouthLayoutRegion(mgr, config);
44836             case "east":
44837                 return new Roo.EastLayoutRegion(mgr, config);
44838             case "west":
44839                 return new Roo.WestLayoutRegion(mgr, config);
44840             case "center":
44841                 return new Roo.CenterLayoutRegion(mgr, config);
44842         }
44843         throw 'Layout region "'+target+'" not supported.';
44844     }
44845 };/*
44846  * Based on:
44847  * Ext JS Library 1.1.1
44848  * Copyright(c) 2006-2007, Ext JS, LLC.
44849  *
44850  * Originally Released Under LGPL - original licence link has changed is not relivant.
44851  *
44852  * Fork - LGPL
44853  * <script type="text/javascript">
44854  */
44855  
44856 /**
44857  * @class Roo.BasicLayoutRegion
44858  * @extends Roo.util.Observable
44859  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
44860  * and does not have a titlebar, tabs or any other features. All it does is size and position 
44861  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
44862  */
44863 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
44864     this.mgr = mgr;
44865     this.position  = pos;
44866     this.events = {
44867         /**
44868          * @scope Roo.BasicLayoutRegion
44869          */
44870         
44871         /**
44872          * @event beforeremove
44873          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
44874          * @param {Roo.LayoutRegion} this
44875          * @param {Roo.ContentPanel} panel The panel
44876          * @param {Object} e The cancel event object
44877          */
44878         "beforeremove" : true,
44879         /**
44880          * @event invalidated
44881          * Fires when the layout for this region is changed.
44882          * @param {Roo.LayoutRegion} this
44883          */
44884         "invalidated" : true,
44885         /**
44886          * @event visibilitychange
44887          * Fires when this region is shown or hidden 
44888          * @param {Roo.LayoutRegion} this
44889          * @param {Boolean} visibility true or false
44890          */
44891         "visibilitychange" : true,
44892         /**
44893          * @event paneladded
44894          * Fires when a panel is added. 
44895          * @param {Roo.LayoutRegion} this
44896          * @param {Roo.ContentPanel} panel The panel
44897          */
44898         "paneladded" : true,
44899         /**
44900          * @event panelremoved
44901          * Fires when a panel is removed. 
44902          * @param {Roo.LayoutRegion} this
44903          * @param {Roo.ContentPanel} panel The panel
44904          */
44905         "panelremoved" : true,
44906         /**
44907          * @event collapsed
44908          * Fires when this region is collapsed.
44909          * @param {Roo.LayoutRegion} this
44910          */
44911         "collapsed" : true,
44912         /**
44913          * @event expanded
44914          * Fires when this region is expanded.
44915          * @param {Roo.LayoutRegion} this
44916          */
44917         "expanded" : true,
44918         /**
44919          * @event slideshow
44920          * Fires when this region is slid into view.
44921          * @param {Roo.LayoutRegion} this
44922          */
44923         "slideshow" : true,
44924         /**
44925          * @event slidehide
44926          * Fires when this region slides out of view. 
44927          * @param {Roo.LayoutRegion} this
44928          */
44929         "slidehide" : true,
44930         /**
44931          * @event panelactivated
44932          * Fires when a panel is activated. 
44933          * @param {Roo.LayoutRegion} this
44934          * @param {Roo.ContentPanel} panel The activated panel
44935          */
44936         "panelactivated" : true,
44937         /**
44938          * @event resized
44939          * Fires when the user resizes this region. 
44940          * @param {Roo.LayoutRegion} this
44941          * @param {Number} newSize The new size (width for east/west, height for north/south)
44942          */
44943         "resized" : true
44944     };
44945     /** A collection of panels in this region. @type Roo.util.MixedCollection */
44946     this.panels = new Roo.util.MixedCollection();
44947     this.panels.getKey = this.getPanelId.createDelegate(this);
44948     this.box = null;
44949     this.activePanel = null;
44950     // ensure listeners are added...
44951     
44952     if (config.listeners || config.events) {
44953         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
44954             listeners : config.listeners || {},
44955             events : config.events || {}
44956         });
44957     }
44958     
44959     if(skipConfig !== true){
44960         this.applyConfig(config);
44961     }
44962 };
44963
44964 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
44965     getPanelId : function(p){
44966         return p.getId();
44967     },
44968     
44969     applyConfig : function(config){
44970         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
44971         this.config = config;
44972         
44973     },
44974     
44975     /**
44976      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
44977      * the width, for horizontal (north, south) the height.
44978      * @param {Number} newSize The new width or height
44979      */
44980     resizeTo : function(newSize){
44981         var el = this.el ? this.el :
44982                  (this.activePanel ? this.activePanel.getEl() : null);
44983         if(el){
44984             switch(this.position){
44985                 case "east":
44986                 case "west":
44987                     el.setWidth(newSize);
44988                     this.fireEvent("resized", this, newSize);
44989                 break;
44990                 case "north":
44991                 case "south":
44992                     el.setHeight(newSize);
44993                     this.fireEvent("resized", this, newSize);
44994                 break;                
44995             }
44996         }
44997     },
44998     
44999     getBox : function(){
45000         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
45001     },
45002     
45003     getMargins : function(){
45004         return this.margins;
45005     },
45006     
45007     updateBox : function(box){
45008         this.box = box;
45009         var el = this.activePanel.getEl();
45010         el.dom.style.left = box.x + "px";
45011         el.dom.style.top = box.y + "px";
45012         this.activePanel.setSize(box.width, box.height);
45013     },
45014     
45015     /**
45016      * Returns the container element for this region.
45017      * @return {Roo.Element}
45018      */
45019     getEl : function(){
45020         return this.activePanel;
45021     },
45022     
45023     /**
45024      * Returns true if this region is currently visible.
45025      * @return {Boolean}
45026      */
45027     isVisible : function(){
45028         return this.activePanel ? true : false;
45029     },
45030     
45031     setActivePanel : function(panel){
45032         panel = this.getPanel(panel);
45033         if(this.activePanel && this.activePanel != panel){
45034             this.activePanel.setActiveState(false);
45035             this.activePanel.getEl().setLeftTop(-10000,-10000);
45036         }
45037         this.activePanel = panel;
45038         panel.setActiveState(true);
45039         if(this.box){
45040             panel.setSize(this.box.width, this.box.height);
45041         }
45042         this.fireEvent("panelactivated", this, panel);
45043         this.fireEvent("invalidated");
45044     },
45045     
45046     /**
45047      * Show the specified panel.
45048      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
45049      * @return {Roo.ContentPanel} The shown panel or null
45050      */
45051     showPanel : function(panel){
45052         if(panel = this.getPanel(panel)){
45053             this.setActivePanel(panel);
45054         }
45055         return panel;
45056     },
45057     
45058     /**
45059      * Get the active panel for this region.
45060      * @return {Roo.ContentPanel} The active panel or null
45061      */
45062     getActivePanel : function(){
45063         return this.activePanel;
45064     },
45065     
45066     /**
45067      * Add the passed ContentPanel(s)
45068      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
45069      * @return {Roo.ContentPanel} The panel added (if only one was added)
45070      */
45071     add : function(panel){
45072         if(arguments.length > 1){
45073             for(var i = 0, len = arguments.length; i < len; i++) {
45074                 this.add(arguments[i]);
45075             }
45076             return null;
45077         }
45078         if(this.hasPanel(panel)){
45079             this.showPanel(panel);
45080             return panel;
45081         }
45082         var el = panel.getEl();
45083         if(el.dom.parentNode != this.mgr.el.dom){
45084             this.mgr.el.dom.appendChild(el.dom);
45085         }
45086         if(panel.setRegion){
45087             panel.setRegion(this);
45088         }
45089         this.panels.add(panel);
45090         el.setStyle("position", "absolute");
45091         if(!panel.background){
45092             this.setActivePanel(panel);
45093             if(this.config.initialSize && this.panels.getCount()==1){
45094                 this.resizeTo(this.config.initialSize);
45095             }
45096         }
45097         this.fireEvent("paneladded", this, panel);
45098         return panel;
45099     },
45100     
45101     /**
45102      * Returns true if the panel is in this region.
45103      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
45104      * @return {Boolean}
45105      */
45106     hasPanel : function(panel){
45107         if(typeof panel == "object"){ // must be panel obj
45108             panel = panel.getId();
45109         }
45110         return this.getPanel(panel) ? true : false;
45111     },
45112     
45113     /**
45114      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
45115      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
45116      * @param {Boolean} preservePanel Overrides the config preservePanel option
45117      * @return {Roo.ContentPanel} The panel that was removed
45118      */
45119     remove : function(panel, preservePanel){
45120         panel = this.getPanel(panel);
45121         if(!panel){
45122             return null;
45123         }
45124         var e = {};
45125         this.fireEvent("beforeremove", this, panel, e);
45126         if(e.cancel === true){
45127             return null;
45128         }
45129         var panelId = panel.getId();
45130         this.panels.removeKey(panelId);
45131         return panel;
45132     },
45133     
45134     /**
45135      * Returns the panel specified or null if it's not in this region.
45136      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
45137      * @return {Roo.ContentPanel}
45138      */
45139     getPanel : function(id){
45140         if(typeof id == "object"){ // must be panel obj
45141             return id;
45142         }
45143         return this.panels.get(id);
45144     },
45145     
45146     /**
45147      * Returns this regions position (north/south/east/west/center).
45148      * @return {String} 
45149      */
45150     getPosition: function(){
45151         return this.position;    
45152     }
45153 });/*
45154  * Based on:
45155  * Ext JS Library 1.1.1
45156  * Copyright(c) 2006-2007, Ext JS, LLC.
45157  *
45158  * Originally Released Under LGPL - original licence link has changed is not relivant.
45159  *
45160  * Fork - LGPL
45161  * <script type="text/javascript">
45162  */
45163  
45164 /**
45165  * @class Roo.LayoutRegion
45166  * @extends Roo.BasicLayoutRegion
45167  * This class represents a region in a layout manager.
45168  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
45169  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
45170  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
45171  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
45172  * @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})
45173  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
45174  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
45175  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
45176  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
45177  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
45178  * @cfg {String}    title           The title for the region (overrides panel titles)
45179  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
45180  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
45181  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
45182  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
45183  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
45184  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
45185  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
45186  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
45187  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
45188  * @cfg {Boolean}   showPin         True to show a pin button
45189  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
45190  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
45191  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
45192  * @cfg {Number}    width           For East/West panels
45193  * @cfg {Number}    height          For North/South panels
45194  * @cfg {Boolean}   split           To show the splitter
45195  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
45196  */
45197 Roo.LayoutRegion = function(mgr, config, pos){
45198     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
45199     var dh = Roo.DomHelper;
45200     /** This region's container element 
45201     * @type Roo.Element */
45202     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
45203     /** This region's title element 
45204     * @type Roo.Element */
45205
45206     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
45207         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
45208         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
45209     ]}, true);
45210     this.titleEl.enableDisplayMode();
45211     /** This region's title text element 
45212     * @type HTMLElement */
45213     this.titleTextEl = this.titleEl.dom.firstChild;
45214     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
45215     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
45216     this.closeBtn.enableDisplayMode();
45217     this.closeBtn.on("click", this.closeClicked, this);
45218     this.closeBtn.hide();
45219
45220     this.createBody(config);
45221     this.visible = true;
45222     this.collapsed = false;
45223
45224     if(config.hideWhenEmpty){
45225         this.hide();
45226         this.on("paneladded", this.validateVisibility, this);
45227         this.on("panelremoved", this.validateVisibility, this);
45228     }
45229     this.applyConfig(config);
45230 };
45231
45232 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
45233
45234     createBody : function(){
45235         /** This region's body element 
45236         * @type Roo.Element */
45237         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
45238     },
45239
45240     applyConfig : function(c){
45241         if(c.collapsible && this.position != "center" && !this.collapsedEl){
45242             var dh = Roo.DomHelper;
45243             if(c.titlebar !== false){
45244                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
45245                 this.collapseBtn.on("click", this.collapse, this);
45246                 this.collapseBtn.enableDisplayMode();
45247
45248                 if(c.showPin === true || this.showPin){
45249                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
45250                     this.stickBtn.enableDisplayMode();
45251                     this.stickBtn.on("click", this.expand, this);
45252                     this.stickBtn.hide();
45253                 }
45254             }
45255             /** This region's collapsed element
45256             * @type Roo.Element */
45257             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
45258                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
45259             ]}, true);
45260             if(c.floatable !== false){
45261                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
45262                this.collapsedEl.on("click", this.collapseClick, this);
45263             }
45264
45265             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
45266                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
45267                    id: "message", unselectable: "on", style:{"float":"left"}});
45268                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
45269              }
45270             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
45271             this.expandBtn.on("click", this.expand, this);
45272         }
45273         if(this.collapseBtn){
45274             this.collapseBtn.setVisible(c.collapsible == true);
45275         }
45276         this.cmargins = c.cmargins || this.cmargins ||
45277                          (this.position == "west" || this.position == "east" ?
45278                              {top: 0, left: 2, right:2, bottom: 0} :
45279                              {top: 2, left: 0, right:0, bottom: 2});
45280         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
45281         this.bottomTabs = c.tabPosition != "top";
45282         this.autoScroll = c.autoScroll || false;
45283         if(this.autoScroll){
45284             this.bodyEl.setStyle("overflow", "auto");
45285         }else{
45286             this.bodyEl.setStyle("overflow", "hidden");
45287         }
45288         //if(c.titlebar !== false){
45289             if((!c.titlebar && !c.title) || c.titlebar === false){
45290                 this.titleEl.hide();
45291             }else{
45292                 this.titleEl.show();
45293                 if(c.title){
45294                     this.titleTextEl.innerHTML = c.title;
45295                 }
45296             }
45297         //}
45298         this.duration = c.duration || .30;
45299         this.slideDuration = c.slideDuration || .45;
45300         this.config = c;
45301         if(c.collapsed){
45302             this.collapse(true);
45303         }
45304         if(c.hidden){
45305             this.hide();
45306         }
45307     },
45308     /**
45309      * Returns true if this region is currently visible.
45310      * @return {Boolean}
45311      */
45312     isVisible : function(){
45313         return this.visible;
45314     },
45315
45316     /**
45317      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
45318      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
45319      */
45320     setCollapsedTitle : function(title){
45321         title = title || "&#160;";
45322         if(this.collapsedTitleTextEl){
45323             this.collapsedTitleTextEl.innerHTML = title;
45324         }
45325     },
45326
45327     getBox : function(){
45328         var b;
45329         if(!this.collapsed){
45330             b = this.el.getBox(false, true);
45331         }else{
45332             b = this.collapsedEl.getBox(false, true);
45333         }
45334         return b;
45335     },
45336
45337     getMargins : function(){
45338         return this.collapsed ? this.cmargins : this.margins;
45339     },
45340
45341     highlight : function(){
45342         this.el.addClass("x-layout-panel-dragover");
45343     },
45344
45345     unhighlight : function(){
45346         this.el.removeClass("x-layout-panel-dragover");
45347     },
45348
45349     updateBox : function(box){
45350         this.box = box;
45351         if(!this.collapsed){
45352             this.el.dom.style.left = box.x + "px";
45353             this.el.dom.style.top = box.y + "px";
45354             this.updateBody(box.width, box.height);
45355         }else{
45356             this.collapsedEl.dom.style.left = box.x + "px";
45357             this.collapsedEl.dom.style.top = box.y + "px";
45358             this.collapsedEl.setSize(box.width, box.height);
45359         }
45360         if(this.tabs){
45361             this.tabs.autoSizeTabs();
45362         }
45363     },
45364
45365     updateBody : function(w, h){
45366         if(w !== null){
45367             this.el.setWidth(w);
45368             w -= this.el.getBorderWidth("rl");
45369             if(this.config.adjustments){
45370                 w += this.config.adjustments[0];
45371             }
45372         }
45373         if(h !== null){
45374             this.el.setHeight(h);
45375             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
45376             h -= this.el.getBorderWidth("tb");
45377             if(this.config.adjustments){
45378                 h += this.config.adjustments[1];
45379             }
45380             this.bodyEl.setHeight(h);
45381             if(this.tabs){
45382                 h = this.tabs.syncHeight(h);
45383             }
45384         }
45385         if(this.panelSize){
45386             w = w !== null ? w : this.panelSize.width;
45387             h = h !== null ? h : this.panelSize.height;
45388         }
45389         if(this.activePanel){
45390             var el = this.activePanel.getEl();
45391             w = w !== null ? w : el.getWidth();
45392             h = h !== null ? h : el.getHeight();
45393             this.panelSize = {width: w, height: h};
45394             this.activePanel.setSize(w, h);
45395         }
45396         if(Roo.isIE && this.tabs){
45397             this.tabs.el.repaint();
45398         }
45399     },
45400
45401     /**
45402      * Returns the container element for this region.
45403      * @return {Roo.Element}
45404      */
45405     getEl : function(){
45406         return this.el;
45407     },
45408
45409     /**
45410      * Hides this region.
45411      */
45412     hide : function(){
45413         if(!this.collapsed){
45414             this.el.dom.style.left = "-2000px";
45415             this.el.hide();
45416         }else{
45417             this.collapsedEl.dom.style.left = "-2000px";
45418             this.collapsedEl.hide();
45419         }
45420         this.visible = false;
45421         this.fireEvent("visibilitychange", this, false);
45422     },
45423
45424     /**
45425      * Shows this region if it was previously hidden.
45426      */
45427     show : function(){
45428         if(!this.collapsed){
45429             this.el.show();
45430         }else{
45431             this.collapsedEl.show();
45432         }
45433         this.visible = true;
45434         this.fireEvent("visibilitychange", this, true);
45435     },
45436
45437     closeClicked : function(){
45438         if(this.activePanel){
45439             this.remove(this.activePanel);
45440         }
45441     },
45442
45443     collapseClick : function(e){
45444         if(this.isSlid){
45445            e.stopPropagation();
45446            this.slideIn();
45447         }else{
45448            e.stopPropagation();
45449            this.slideOut();
45450         }
45451     },
45452
45453     /**
45454      * Collapses this region.
45455      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
45456      */
45457     collapse : function(skipAnim){
45458         if(this.collapsed) return;
45459         this.collapsed = true;
45460         if(this.split){
45461             this.split.el.hide();
45462         }
45463         if(this.config.animate && skipAnim !== true){
45464             this.fireEvent("invalidated", this);
45465             this.animateCollapse();
45466         }else{
45467             this.el.setLocation(-20000,-20000);
45468             this.el.hide();
45469             this.collapsedEl.show();
45470             this.fireEvent("collapsed", this);
45471             this.fireEvent("invalidated", this);
45472         }
45473     },
45474
45475     animateCollapse : function(){
45476         // overridden
45477     },
45478
45479     /**
45480      * Expands this region if it was previously collapsed.
45481      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
45482      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
45483      */
45484     expand : function(e, skipAnim){
45485         if(e) e.stopPropagation();
45486         if(!this.collapsed || this.el.hasActiveFx()) return;
45487         if(this.isSlid){
45488             this.afterSlideIn();
45489             skipAnim = true;
45490         }
45491         this.collapsed = false;
45492         if(this.config.animate && skipAnim !== true){
45493             this.animateExpand();
45494         }else{
45495             this.el.show();
45496             if(this.split){
45497                 this.split.el.show();
45498             }
45499             this.collapsedEl.setLocation(-2000,-2000);
45500             this.collapsedEl.hide();
45501             this.fireEvent("invalidated", this);
45502             this.fireEvent("expanded", this);
45503         }
45504     },
45505
45506     animateExpand : function(){
45507         // overridden
45508     },
45509
45510     initTabs : function()
45511     {
45512         this.bodyEl.setStyle("overflow", "hidden");
45513         var ts = new Roo.TabPanel(
45514                 this.bodyEl.dom,
45515                 {
45516                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
45517                     disableTooltips: this.config.disableTabTips,
45518                     toolbar : this.config.toolbar
45519                 }
45520         );
45521         if(this.config.hideTabs){
45522             ts.stripWrap.setDisplayed(false);
45523         }
45524         this.tabs = ts;
45525         ts.resizeTabs = this.config.resizeTabs === true;
45526         ts.minTabWidth = this.config.minTabWidth || 40;
45527         ts.maxTabWidth = this.config.maxTabWidth || 250;
45528         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
45529         ts.monitorResize = false;
45530         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
45531         ts.bodyEl.addClass('x-layout-tabs-body');
45532         this.panels.each(this.initPanelAsTab, this);
45533     },
45534
45535     initPanelAsTab : function(panel){
45536         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
45537                     this.config.closeOnTab && panel.isClosable());
45538         if(panel.tabTip !== undefined){
45539             ti.setTooltip(panel.tabTip);
45540         }
45541         ti.on("activate", function(){
45542               this.setActivePanel(panel);
45543         }, this);
45544         if(this.config.closeOnTab){
45545             ti.on("beforeclose", function(t, e){
45546                 e.cancel = true;
45547                 this.remove(panel);
45548             }, this);
45549         }
45550         return ti;
45551     },
45552
45553     updatePanelTitle : function(panel, title){
45554         if(this.activePanel == panel){
45555             this.updateTitle(title);
45556         }
45557         if(this.tabs){
45558             var ti = this.tabs.getTab(panel.getEl().id);
45559             ti.setText(title);
45560             if(panel.tabTip !== undefined){
45561                 ti.setTooltip(panel.tabTip);
45562             }
45563         }
45564     },
45565
45566     updateTitle : function(title){
45567         if(this.titleTextEl && !this.config.title){
45568             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
45569         }
45570     },
45571
45572     setActivePanel : function(panel){
45573         panel = this.getPanel(panel);
45574         if(this.activePanel && this.activePanel != panel){
45575             this.activePanel.setActiveState(false);
45576         }
45577         this.activePanel = panel;
45578         panel.setActiveState(true);
45579         if(this.panelSize){
45580             panel.setSize(this.panelSize.width, this.panelSize.height);
45581         }
45582         if(this.closeBtn){
45583             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
45584         }
45585         this.updateTitle(panel.getTitle());
45586         if(this.tabs){
45587             this.fireEvent("invalidated", this);
45588         }
45589         this.fireEvent("panelactivated", this, panel);
45590     },
45591
45592     /**
45593      * Shows the specified panel.
45594      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
45595      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
45596      */
45597     showPanel : function(panel){
45598         if(panel = this.getPanel(panel)){
45599             if(this.tabs){
45600                 var tab = this.tabs.getTab(panel.getEl().id);
45601                 if(tab.isHidden()){
45602                     this.tabs.unhideTab(tab.id);
45603                 }
45604                 tab.activate();
45605             }else{
45606                 this.setActivePanel(panel);
45607             }
45608         }
45609         return panel;
45610     },
45611
45612     /**
45613      * Get the active panel for this region.
45614      * @return {Roo.ContentPanel} The active panel or null
45615      */
45616     getActivePanel : function(){
45617         return this.activePanel;
45618     },
45619
45620     validateVisibility : function(){
45621         if(this.panels.getCount() < 1){
45622             this.updateTitle("&#160;");
45623             this.closeBtn.hide();
45624             this.hide();
45625         }else{
45626             if(!this.isVisible()){
45627                 this.show();
45628             }
45629         }
45630     },
45631
45632     /**
45633      * Adds the passed ContentPanel(s) to this region.
45634      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
45635      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
45636      */
45637     add : function(panel){
45638         if(arguments.length > 1){
45639             for(var i = 0, len = arguments.length; i < len; i++) {
45640                 this.add(arguments[i]);
45641             }
45642             return null;
45643         }
45644         if(this.hasPanel(panel)){
45645             this.showPanel(panel);
45646             return panel;
45647         }
45648         panel.setRegion(this);
45649         this.panels.add(panel);
45650         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
45651             this.bodyEl.dom.appendChild(panel.getEl().dom);
45652             if(panel.background !== true){
45653                 this.setActivePanel(panel);
45654             }
45655             this.fireEvent("paneladded", this, panel);
45656             return panel;
45657         }
45658         if(!this.tabs){
45659             this.initTabs();
45660         }else{
45661             this.initPanelAsTab(panel);
45662         }
45663         if(panel.background !== true){
45664             this.tabs.activate(panel.getEl().id);
45665         }
45666         this.fireEvent("paneladded", this, panel);
45667         return panel;
45668     },
45669
45670     /**
45671      * Hides the tab for the specified panel.
45672      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
45673      */
45674     hidePanel : function(panel){
45675         if(this.tabs && (panel = this.getPanel(panel))){
45676             this.tabs.hideTab(panel.getEl().id);
45677         }
45678     },
45679
45680     /**
45681      * Unhides the tab for a previously hidden panel.
45682      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
45683      */
45684     unhidePanel : function(panel){
45685         if(this.tabs && (panel = this.getPanel(panel))){
45686             this.tabs.unhideTab(panel.getEl().id);
45687         }
45688     },
45689
45690     clearPanels : function(){
45691         while(this.panels.getCount() > 0){
45692              this.remove(this.panels.first());
45693         }
45694     },
45695
45696     /**
45697      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
45698      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
45699      * @param {Boolean} preservePanel Overrides the config preservePanel option
45700      * @return {Roo.ContentPanel} The panel that was removed
45701      */
45702     remove : function(panel, preservePanel){
45703         panel = this.getPanel(panel);
45704         if(!panel){
45705             return null;
45706         }
45707         var e = {};
45708         this.fireEvent("beforeremove", this, panel, e);
45709         if(e.cancel === true){
45710             return null;
45711         }
45712         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
45713         var panelId = panel.getId();
45714         this.panels.removeKey(panelId);
45715         if(preservePanel){
45716             document.body.appendChild(panel.getEl().dom);
45717         }
45718         if(this.tabs){
45719             this.tabs.removeTab(panel.getEl().id);
45720         }else if (!preservePanel){
45721             this.bodyEl.dom.removeChild(panel.getEl().dom);
45722         }
45723         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
45724             var p = this.panels.first();
45725             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
45726             tempEl.appendChild(p.getEl().dom);
45727             this.bodyEl.update("");
45728             this.bodyEl.dom.appendChild(p.getEl().dom);
45729             tempEl = null;
45730             this.updateTitle(p.getTitle());
45731             this.tabs = null;
45732             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
45733             this.setActivePanel(p);
45734         }
45735         panel.setRegion(null);
45736         if(this.activePanel == panel){
45737             this.activePanel = null;
45738         }
45739         if(this.config.autoDestroy !== false && preservePanel !== true){
45740             try{panel.destroy();}catch(e){}
45741         }
45742         this.fireEvent("panelremoved", this, panel);
45743         return panel;
45744     },
45745
45746     /**
45747      * Returns the TabPanel component used by this region
45748      * @return {Roo.TabPanel}
45749      */
45750     getTabs : function(){
45751         return this.tabs;
45752     },
45753
45754     createTool : function(parentEl, className){
45755         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
45756             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
45757         btn.addClassOnOver("x-layout-tools-button-over");
45758         return btn;
45759     }
45760 });/*
45761  * Based on:
45762  * Ext JS Library 1.1.1
45763  * Copyright(c) 2006-2007, Ext JS, LLC.
45764  *
45765  * Originally Released Under LGPL - original licence link has changed is not relivant.
45766  *
45767  * Fork - LGPL
45768  * <script type="text/javascript">
45769  */
45770  
45771
45772
45773 /**
45774  * @class Roo.SplitLayoutRegion
45775  * @extends Roo.LayoutRegion
45776  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
45777  */
45778 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
45779     this.cursor = cursor;
45780     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
45781 };
45782
45783 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
45784     splitTip : "Drag to resize.",
45785     collapsibleSplitTip : "Drag to resize. Double click to hide.",
45786     useSplitTips : false,
45787
45788     applyConfig : function(config){
45789         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
45790         if(config.split){
45791             if(!this.split){
45792                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
45793                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
45794                 /** The SplitBar for this region 
45795                 * @type Roo.SplitBar */
45796                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
45797                 this.split.on("moved", this.onSplitMove, this);
45798                 this.split.useShim = config.useShim === true;
45799                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
45800                 if(this.useSplitTips){
45801                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
45802                 }
45803                 if(config.collapsible){
45804                     this.split.el.on("dblclick", this.collapse,  this);
45805                 }
45806             }
45807             if(typeof config.minSize != "undefined"){
45808                 this.split.minSize = config.minSize;
45809             }
45810             if(typeof config.maxSize != "undefined"){
45811                 this.split.maxSize = config.maxSize;
45812             }
45813             if(config.hideWhenEmpty || config.hidden || config.collapsed){
45814                 this.hideSplitter();
45815             }
45816         }
45817     },
45818
45819     getHMaxSize : function(){
45820          var cmax = this.config.maxSize || 10000;
45821          var center = this.mgr.getRegion("center");
45822          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
45823     },
45824
45825     getVMaxSize : function(){
45826          var cmax = this.config.maxSize || 10000;
45827          var center = this.mgr.getRegion("center");
45828          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
45829     },
45830
45831     onSplitMove : function(split, newSize){
45832         this.fireEvent("resized", this, newSize);
45833     },
45834     
45835     /** 
45836      * Returns the {@link Roo.SplitBar} for this region.
45837      * @return {Roo.SplitBar}
45838      */
45839     getSplitBar : function(){
45840         return this.split;
45841     },
45842     
45843     hide : function(){
45844         this.hideSplitter();
45845         Roo.SplitLayoutRegion.superclass.hide.call(this);
45846     },
45847
45848     hideSplitter : function(){
45849         if(this.split){
45850             this.split.el.setLocation(-2000,-2000);
45851             this.split.el.hide();
45852         }
45853     },
45854
45855     show : function(){
45856         if(this.split){
45857             this.split.el.show();
45858         }
45859         Roo.SplitLayoutRegion.superclass.show.call(this);
45860     },
45861     
45862     beforeSlide: function(){
45863         if(Roo.isGecko){// firefox overflow auto bug workaround
45864             this.bodyEl.clip();
45865             if(this.tabs) this.tabs.bodyEl.clip();
45866             if(this.activePanel){
45867                 this.activePanel.getEl().clip();
45868                 
45869                 if(this.activePanel.beforeSlide){
45870                     this.activePanel.beforeSlide();
45871                 }
45872             }
45873         }
45874     },
45875     
45876     afterSlide : function(){
45877         if(Roo.isGecko){// firefox overflow auto bug workaround
45878             this.bodyEl.unclip();
45879             if(this.tabs) this.tabs.bodyEl.unclip();
45880             if(this.activePanel){
45881                 this.activePanel.getEl().unclip();
45882                 if(this.activePanel.afterSlide){
45883                     this.activePanel.afterSlide();
45884                 }
45885             }
45886         }
45887     },
45888
45889     initAutoHide : function(){
45890         if(this.autoHide !== false){
45891             if(!this.autoHideHd){
45892                 var st = new Roo.util.DelayedTask(this.slideIn, this);
45893                 this.autoHideHd = {
45894                     "mouseout": function(e){
45895                         if(!e.within(this.el, true)){
45896                             st.delay(500);
45897                         }
45898                     },
45899                     "mouseover" : function(e){
45900                         st.cancel();
45901                     },
45902                     scope : this
45903                 };
45904             }
45905             this.el.on(this.autoHideHd);
45906         }
45907     },
45908
45909     clearAutoHide : function(){
45910         if(this.autoHide !== false){
45911             this.el.un("mouseout", this.autoHideHd.mouseout);
45912             this.el.un("mouseover", this.autoHideHd.mouseover);
45913         }
45914     },
45915
45916     clearMonitor : function(){
45917         Roo.get(document).un("click", this.slideInIf, this);
45918     },
45919
45920     // these names are backwards but not changed for compat
45921     slideOut : function(){
45922         if(this.isSlid || this.el.hasActiveFx()){
45923             return;
45924         }
45925         this.isSlid = true;
45926         if(this.collapseBtn){
45927             this.collapseBtn.hide();
45928         }
45929         this.closeBtnState = this.closeBtn.getStyle('display');
45930         this.closeBtn.hide();
45931         if(this.stickBtn){
45932             this.stickBtn.show();
45933         }
45934         this.el.show();
45935         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
45936         this.beforeSlide();
45937         this.el.setStyle("z-index", 10001);
45938         this.el.slideIn(this.getSlideAnchor(), {
45939             callback: function(){
45940                 this.afterSlide();
45941                 this.initAutoHide();
45942                 Roo.get(document).on("click", this.slideInIf, this);
45943                 this.fireEvent("slideshow", this);
45944             },
45945             scope: this,
45946             block: true
45947         });
45948     },
45949
45950     afterSlideIn : function(){
45951         this.clearAutoHide();
45952         this.isSlid = false;
45953         this.clearMonitor();
45954         this.el.setStyle("z-index", "");
45955         if(this.collapseBtn){
45956             this.collapseBtn.show();
45957         }
45958         this.closeBtn.setStyle('display', this.closeBtnState);
45959         if(this.stickBtn){
45960             this.stickBtn.hide();
45961         }
45962         this.fireEvent("slidehide", this);
45963     },
45964
45965     slideIn : function(cb){
45966         if(!this.isSlid || this.el.hasActiveFx()){
45967             Roo.callback(cb);
45968             return;
45969         }
45970         this.isSlid = false;
45971         this.beforeSlide();
45972         this.el.slideOut(this.getSlideAnchor(), {
45973             callback: function(){
45974                 this.el.setLeftTop(-10000, -10000);
45975                 this.afterSlide();
45976                 this.afterSlideIn();
45977                 Roo.callback(cb);
45978             },
45979             scope: this,
45980             block: true
45981         });
45982     },
45983     
45984     slideInIf : function(e){
45985         if(!e.within(this.el)){
45986             this.slideIn();
45987         }
45988     },
45989
45990     animateCollapse : function(){
45991         this.beforeSlide();
45992         this.el.setStyle("z-index", 20000);
45993         var anchor = this.getSlideAnchor();
45994         this.el.slideOut(anchor, {
45995             callback : function(){
45996                 this.el.setStyle("z-index", "");
45997                 this.collapsedEl.slideIn(anchor, {duration:.3});
45998                 this.afterSlide();
45999                 this.el.setLocation(-10000,-10000);
46000                 this.el.hide();
46001                 this.fireEvent("collapsed", this);
46002             },
46003             scope: this,
46004             block: true
46005         });
46006     },
46007
46008     animateExpand : function(){
46009         this.beforeSlide();
46010         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
46011         this.el.setStyle("z-index", 20000);
46012         this.collapsedEl.hide({
46013             duration:.1
46014         });
46015         this.el.slideIn(this.getSlideAnchor(), {
46016             callback : function(){
46017                 this.el.setStyle("z-index", "");
46018                 this.afterSlide();
46019                 if(this.split){
46020                     this.split.el.show();
46021                 }
46022                 this.fireEvent("invalidated", this);
46023                 this.fireEvent("expanded", this);
46024             },
46025             scope: this,
46026             block: true
46027         });
46028     },
46029
46030     anchors : {
46031         "west" : "left",
46032         "east" : "right",
46033         "north" : "top",
46034         "south" : "bottom"
46035     },
46036
46037     sanchors : {
46038         "west" : "l",
46039         "east" : "r",
46040         "north" : "t",
46041         "south" : "b"
46042     },
46043
46044     canchors : {
46045         "west" : "tl-tr",
46046         "east" : "tr-tl",
46047         "north" : "tl-bl",
46048         "south" : "bl-tl"
46049     },
46050
46051     getAnchor : function(){
46052         return this.anchors[this.position];
46053     },
46054
46055     getCollapseAnchor : function(){
46056         return this.canchors[this.position];
46057     },
46058
46059     getSlideAnchor : function(){
46060         return this.sanchors[this.position];
46061     },
46062
46063     getAlignAdj : function(){
46064         var cm = this.cmargins;
46065         switch(this.position){
46066             case "west":
46067                 return [0, 0];
46068             break;
46069             case "east":
46070                 return [0, 0];
46071             break;
46072             case "north":
46073                 return [0, 0];
46074             break;
46075             case "south":
46076                 return [0, 0];
46077             break;
46078         }
46079     },
46080
46081     getExpandAdj : function(){
46082         var c = this.collapsedEl, cm = this.cmargins;
46083         switch(this.position){
46084             case "west":
46085                 return [-(cm.right+c.getWidth()+cm.left), 0];
46086             break;
46087             case "east":
46088                 return [cm.right+c.getWidth()+cm.left, 0];
46089             break;
46090             case "north":
46091                 return [0, -(cm.top+cm.bottom+c.getHeight())];
46092             break;
46093             case "south":
46094                 return [0, cm.top+cm.bottom+c.getHeight()];
46095             break;
46096         }
46097     }
46098 });/*
46099  * Based on:
46100  * Ext JS Library 1.1.1
46101  * Copyright(c) 2006-2007, Ext JS, LLC.
46102  *
46103  * Originally Released Under LGPL - original licence link has changed is not relivant.
46104  *
46105  * Fork - LGPL
46106  * <script type="text/javascript">
46107  */
46108 /*
46109  * These classes are private internal classes
46110  */
46111 Roo.CenterLayoutRegion = function(mgr, config){
46112     Roo.LayoutRegion.call(this, mgr, config, "center");
46113     this.visible = true;
46114     this.minWidth = config.minWidth || 20;
46115     this.minHeight = config.minHeight || 20;
46116 };
46117
46118 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
46119     hide : function(){
46120         // center panel can't be hidden
46121     },
46122     
46123     show : function(){
46124         // center panel can't be hidden
46125     },
46126     
46127     getMinWidth: function(){
46128         return this.minWidth;
46129     },
46130     
46131     getMinHeight: function(){
46132         return this.minHeight;
46133     }
46134 });
46135
46136
46137 Roo.NorthLayoutRegion = function(mgr, config){
46138     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
46139     if(this.split){
46140         this.split.placement = Roo.SplitBar.TOP;
46141         this.split.orientation = Roo.SplitBar.VERTICAL;
46142         this.split.el.addClass("x-layout-split-v");
46143     }
46144     var size = config.initialSize || config.height;
46145     if(typeof size != "undefined"){
46146         this.el.setHeight(size);
46147     }
46148 };
46149 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
46150     orientation: Roo.SplitBar.VERTICAL,
46151     getBox : function(){
46152         if(this.collapsed){
46153             return this.collapsedEl.getBox();
46154         }
46155         var box = this.el.getBox();
46156         if(this.split){
46157             box.height += this.split.el.getHeight();
46158         }
46159         return box;
46160     },
46161     
46162     updateBox : function(box){
46163         if(this.split && !this.collapsed){
46164             box.height -= this.split.el.getHeight();
46165             this.split.el.setLeft(box.x);
46166             this.split.el.setTop(box.y+box.height);
46167             this.split.el.setWidth(box.width);
46168         }
46169         if(this.collapsed){
46170             this.updateBody(box.width, null);
46171         }
46172         Roo.LayoutRegion.prototype.updateBox.call(this, box);
46173     }
46174 });
46175
46176 Roo.SouthLayoutRegion = function(mgr, config){
46177     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
46178     if(this.split){
46179         this.split.placement = Roo.SplitBar.BOTTOM;
46180         this.split.orientation = Roo.SplitBar.VERTICAL;
46181         this.split.el.addClass("x-layout-split-v");
46182     }
46183     var size = config.initialSize || config.height;
46184     if(typeof size != "undefined"){
46185         this.el.setHeight(size);
46186     }
46187 };
46188 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
46189     orientation: Roo.SplitBar.VERTICAL,
46190     getBox : function(){
46191         if(this.collapsed){
46192             return this.collapsedEl.getBox();
46193         }
46194         var box = this.el.getBox();
46195         if(this.split){
46196             var sh = this.split.el.getHeight();
46197             box.height += sh;
46198             box.y -= sh;
46199         }
46200         return box;
46201     },
46202     
46203     updateBox : function(box){
46204         if(this.split && !this.collapsed){
46205             var sh = this.split.el.getHeight();
46206             box.height -= sh;
46207             box.y += sh;
46208             this.split.el.setLeft(box.x);
46209             this.split.el.setTop(box.y-sh);
46210             this.split.el.setWidth(box.width);
46211         }
46212         if(this.collapsed){
46213             this.updateBody(box.width, null);
46214         }
46215         Roo.LayoutRegion.prototype.updateBox.call(this, box);
46216     }
46217 });
46218
46219 Roo.EastLayoutRegion = function(mgr, config){
46220     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
46221     if(this.split){
46222         this.split.placement = Roo.SplitBar.RIGHT;
46223         this.split.orientation = Roo.SplitBar.HORIZONTAL;
46224         this.split.el.addClass("x-layout-split-h");
46225     }
46226     var size = config.initialSize || config.width;
46227     if(typeof size != "undefined"){
46228         this.el.setWidth(size);
46229     }
46230 };
46231 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
46232     orientation: Roo.SplitBar.HORIZONTAL,
46233     getBox : function(){
46234         if(this.collapsed){
46235             return this.collapsedEl.getBox();
46236         }
46237         var box = this.el.getBox();
46238         if(this.split){
46239             var sw = this.split.el.getWidth();
46240             box.width += sw;
46241             box.x -= sw;
46242         }
46243         return box;
46244     },
46245
46246     updateBox : function(box){
46247         if(this.split && !this.collapsed){
46248             var sw = this.split.el.getWidth();
46249             box.width -= sw;
46250             this.split.el.setLeft(box.x);
46251             this.split.el.setTop(box.y);
46252             this.split.el.setHeight(box.height);
46253             box.x += sw;
46254         }
46255         if(this.collapsed){
46256             this.updateBody(null, box.height);
46257         }
46258         Roo.LayoutRegion.prototype.updateBox.call(this, box);
46259     }
46260 });
46261
46262 Roo.WestLayoutRegion = function(mgr, config){
46263     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
46264     if(this.split){
46265         this.split.placement = Roo.SplitBar.LEFT;
46266         this.split.orientation = Roo.SplitBar.HORIZONTAL;
46267         this.split.el.addClass("x-layout-split-h");
46268     }
46269     var size = config.initialSize || config.width;
46270     if(typeof size != "undefined"){
46271         this.el.setWidth(size);
46272     }
46273 };
46274 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
46275     orientation: Roo.SplitBar.HORIZONTAL,
46276     getBox : function(){
46277         if(this.collapsed){
46278             return this.collapsedEl.getBox();
46279         }
46280         var box = this.el.getBox();
46281         if(this.split){
46282             box.width += this.split.el.getWidth();
46283         }
46284         return box;
46285     },
46286     
46287     updateBox : function(box){
46288         if(this.split && !this.collapsed){
46289             var sw = this.split.el.getWidth();
46290             box.width -= sw;
46291             this.split.el.setLeft(box.x+box.width);
46292             this.split.el.setTop(box.y);
46293             this.split.el.setHeight(box.height);
46294         }
46295         if(this.collapsed){
46296             this.updateBody(null, box.height);
46297         }
46298         Roo.LayoutRegion.prototype.updateBox.call(this, box);
46299     }
46300 });
46301 /*
46302  * Based on:
46303  * Ext JS Library 1.1.1
46304  * Copyright(c) 2006-2007, Ext JS, LLC.
46305  *
46306  * Originally Released Under LGPL - original licence link has changed is not relivant.
46307  *
46308  * Fork - LGPL
46309  * <script type="text/javascript">
46310  */
46311  
46312  
46313 /*
46314  * Private internal class for reading and applying state
46315  */
46316 Roo.LayoutStateManager = function(layout){
46317      // default empty state
46318      this.state = {
46319         north: {},
46320         south: {},
46321         east: {},
46322         west: {}       
46323     };
46324 };
46325
46326 Roo.LayoutStateManager.prototype = {
46327     init : function(layout, provider){
46328         this.provider = provider;
46329         var state = provider.get(layout.id+"-layout-state");
46330         if(state){
46331             var wasUpdating = layout.isUpdating();
46332             if(!wasUpdating){
46333                 layout.beginUpdate();
46334             }
46335             for(var key in state){
46336                 if(typeof state[key] != "function"){
46337                     var rstate = state[key];
46338                     var r = layout.getRegion(key);
46339                     if(r && rstate){
46340                         if(rstate.size){
46341                             r.resizeTo(rstate.size);
46342                         }
46343                         if(rstate.collapsed == true){
46344                             r.collapse(true);
46345                         }else{
46346                             r.expand(null, true);
46347                         }
46348                     }
46349                 }
46350             }
46351             if(!wasUpdating){
46352                 layout.endUpdate();
46353             }
46354             this.state = state; 
46355         }
46356         this.layout = layout;
46357         layout.on("regionresized", this.onRegionResized, this);
46358         layout.on("regioncollapsed", this.onRegionCollapsed, this);
46359         layout.on("regionexpanded", this.onRegionExpanded, this);
46360     },
46361     
46362     storeState : function(){
46363         this.provider.set(this.layout.id+"-layout-state", this.state);
46364     },
46365     
46366     onRegionResized : function(region, newSize){
46367         this.state[region.getPosition()].size = newSize;
46368         this.storeState();
46369     },
46370     
46371     onRegionCollapsed : function(region){
46372         this.state[region.getPosition()].collapsed = true;
46373         this.storeState();
46374     },
46375     
46376     onRegionExpanded : function(region){
46377         this.state[region.getPosition()].collapsed = false;
46378         this.storeState();
46379     }
46380 };/*
46381  * Based on:
46382  * Ext JS Library 1.1.1
46383  * Copyright(c) 2006-2007, Ext JS, LLC.
46384  *
46385  * Originally Released Under LGPL - original licence link has changed is not relivant.
46386  *
46387  * Fork - LGPL
46388  * <script type="text/javascript">
46389  */
46390 /**
46391  * @class Roo.ContentPanel
46392  * @extends Roo.util.Observable
46393  * A basic ContentPanel element.
46394  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
46395  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
46396  * @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
46397  * @cfg {Boolean}   closable      True if the panel can be closed/removed
46398  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
46399  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
46400  * @cfg {Toolbar}   toolbar       A toolbar for this panel
46401  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
46402  * @cfg {String} title          The title for this panel
46403  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
46404  * @cfg {String} url            Calls {@link #setUrl} with this value
46405  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
46406  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
46407  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
46408  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
46409
46410  * @constructor
46411  * Create a new ContentPanel.
46412  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
46413  * @param {String/Object} config A string to set only the title or a config object
46414  * @param {String} content (optional) Set the HTML content for this panel
46415  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
46416  */
46417 Roo.ContentPanel = function(el, config, content){
46418     
46419      
46420     /*
46421     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
46422         config = el;
46423         el = Roo.id();
46424     }
46425     if (config && config.parentLayout) { 
46426         el = config.parentLayout.el.createChild(); 
46427     }
46428     */
46429     if(el.autoCreate){ // xtype is available if this is called from factory
46430         config = el;
46431         el = Roo.id();
46432     }
46433     this.el = Roo.get(el);
46434     if(!this.el && config && config.autoCreate){
46435         if(typeof config.autoCreate == "object"){
46436             if(!config.autoCreate.id){
46437                 config.autoCreate.id = config.id||el;
46438             }
46439             this.el = Roo.DomHelper.append(document.body,
46440                         config.autoCreate, true);
46441         }else{
46442             this.el = Roo.DomHelper.append(document.body,
46443                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
46444         }
46445     }
46446     this.closable = false;
46447     this.loaded = false;
46448     this.active = false;
46449     if(typeof config == "string"){
46450         this.title = config;
46451     }else{
46452         Roo.apply(this, config);
46453     }
46454     
46455     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
46456         this.wrapEl = this.el.wrap();
46457         this.toolbar.container = this.el.insertSibling(false, 'before');
46458         this.toolbar = new Roo.Toolbar(this.toolbar);
46459     }
46460     
46461     
46462     
46463     if(this.resizeEl){
46464         this.resizeEl = Roo.get(this.resizeEl, true);
46465     }else{
46466         this.resizeEl = this.el;
46467     }
46468     this.addEvents({
46469         /**
46470          * @event activate
46471          * Fires when this panel is activated. 
46472          * @param {Roo.ContentPanel} this
46473          */
46474         "activate" : true,
46475         /**
46476          * @event deactivate
46477          * Fires when this panel is activated. 
46478          * @param {Roo.ContentPanel} this
46479          */
46480         "deactivate" : true,
46481
46482         /**
46483          * @event resize
46484          * Fires when this panel is resized if fitToFrame is true.
46485          * @param {Roo.ContentPanel} this
46486          * @param {Number} width The width after any component adjustments
46487          * @param {Number} height The height after any component adjustments
46488          */
46489         "resize" : true,
46490         
46491          /**
46492          * @event render
46493          * Fires when this tab is created
46494          * @param {Roo.ContentPanel} this
46495          */
46496         "render" : true
46497         
46498         
46499         
46500     });
46501     if(this.autoScroll){
46502         this.resizeEl.setStyle("overflow", "auto");
46503     } else {
46504         // fix randome scrolling
46505         this.el.on('scroll', function() {
46506             Roo.log('fix random scolling');
46507             this.scrollTo('top',0); 
46508         });
46509     }
46510     content = content || this.content;
46511     if(content){
46512         this.setContent(content);
46513     }
46514     if(config && config.url){
46515         this.setUrl(this.url, this.params, this.loadOnce);
46516     }
46517     
46518     
46519     
46520     Roo.ContentPanel.superclass.constructor.call(this);
46521     
46522     this.fireEvent('render', this);
46523 };
46524
46525 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
46526     tabTip:'',
46527     setRegion : function(region){
46528         this.region = region;
46529         if(region){
46530            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
46531         }else{
46532            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
46533         } 
46534     },
46535     
46536     /**
46537      * Returns the toolbar for this Panel if one was configured. 
46538      * @return {Roo.Toolbar} 
46539      */
46540     getToolbar : function(){
46541         return this.toolbar;
46542     },
46543     
46544     setActiveState : function(active){
46545         this.active = active;
46546         if(!active){
46547             this.fireEvent("deactivate", this);
46548         }else{
46549             this.fireEvent("activate", this);
46550         }
46551     },
46552     /**
46553      * Updates this panel's element
46554      * @param {String} content The new content
46555      * @param {Boolean} loadScripts (optional) true to look for and process scripts
46556     */
46557     setContent : function(content, loadScripts){
46558         this.el.update(content, loadScripts);
46559     },
46560
46561     ignoreResize : function(w, h){
46562         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
46563             return true;
46564         }else{
46565             this.lastSize = {width: w, height: h};
46566             return false;
46567         }
46568     },
46569     /**
46570      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
46571      * @return {Roo.UpdateManager} The UpdateManager
46572      */
46573     getUpdateManager : function(){
46574         return this.el.getUpdateManager();
46575     },
46576      /**
46577      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
46578      * @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:
46579 <pre><code>
46580 panel.load({
46581     url: "your-url.php",
46582     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
46583     callback: yourFunction,
46584     scope: yourObject, //(optional scope)
46585     discardUrl: false,
46586     nocache: false,
46587     text: "Loading...",
46588     timeout: 30,
46589     scripts: false
46590 });
46591 </code></pre>
46592      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
46593      * 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.
46594      * @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}
46595      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
46596      * @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.
46597      * @return {Roo.ContentPanel} this
46598      */
46599     load : function(){
46600         var um = this.el.getUpdateManager();
46601         um.update.apply(um, arguments);
46602         return this;
46603     },
46604
46605
46606     /**
46607      * 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.
46608      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
46609      * @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)
46610      * @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)
46611      * @return {Roo.UpdateManager} The UpdateManager
46612      */
46613     setUrl : function(url, params, loadOnce){
46614         if(this.refreshDelegate){
46615             this.removeListener("activate", this.refreshDelegate);
46616         }
46617         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
46618         this.on("activate", this.refreshDelegate);
46619         return this.el.getUpdateManager();
46620     },
46621     
46622     _handleRefresh : function(url, params, loadOnce){
46623         if(!loadOnce || !this.loaded){
46624             var updater = this.el.getUpdateManager();
46625             updater.update(url, params, this._setLoaded.createDelegate(this));
46626         }
46627     },
46628     
46629     _setLoaded : function(){
46630         this.loaded = true;
46631     }, 
46632     
46633     /**
46634      * Returns this panel's id
46635      * @return {String} 
46636      */
46637     getId : function(){
46638         return this.el.id;
46639     },
46640     
46641     /** 
46642      * Returns this panel's element - used by regiosn to add.
46643      * @return {Roo.Element} 
46644      */
46645     getEl : function(){
46646         return this.wrapEl || this.el;
46647     },
46648     
46649     adjustForComponents : function(width, height){
46650         if(this.resizeEl != this.el){
46651             width -= this.el.getFrameWidth('lr');
46652             height -= this.el.getFrameWidth('tb');
46653         }
46654         if(this.toolbar){
46655             var te = this.toolbar.getEl();
46656             height -= te.getHeight();
46657             te.setWidth(width);
46658         }
46659         if(this.adjustments){
46660             width += this.adjustments[0];
46661             height += this.adjustments[1];
46662         }
46663         return {"width": width, "height": height};
46664     },
46665     
46666     setSize : function(width, height){
46667         if(this.fitToFrame && !this.ignoreResize(width, height)){
46668             if(this.fitContainer && this.resizeEl != this.el){
46669                 this.el.setSize(width, height);
46670             }
46671             var size = this.adjustForComponents(width, height);
46672             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
46673             this.fireEvent('resize', this, size.width, size.height);
46674         }
46675     },
46676     
46677     /**
46678      * Returns this panel's title
46679      * @return {String} 
46680      */
46681     getTitle : function(){
46682         return this.title;
46683     },
46684     
46685     /**
46686      * Set this panel's title
46687      * @param {String} title
46688      */
46689     setTitle : function(title){
46690         this.title = title;
46691         if(this.region){
46692             this.region.updatePanelTitle(this, title);
46693         }
46694     },
46695     
46696     /**
46697      * Returns true is this panel was configured to be closable
46698      * @return {Boolean} 
46699      */
46700     isClosable : function(){
46701         return this.closable;
46702     },
46703     
46704     beforeSlide : function(){
46705         this.el.clip();
46706         this.resizeEl.clip();
46707     },
46708     
46709     afterSlide : function(){
46710         this.el.unclip();
46711         this.resizeEl.unclip();
46712     },
46713     
46714     /**
46715      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
46716      *   Will fail silently if the {@link #setUrl} method has not been called.
46717      *   This does not activate the panel, just updates its content.
46718      */
46719     refresh : function(){
46720         if(this.refreshDelegate){
46721            this.loaded = false;
46722            this.refreshDelegate();
46723         }
46724     },
46725     
46726     /**
46727      * Destroys this panel
46728      */
46729     destroy : function(){
46730         this.el.removeAllListeners();
46731         var tempEl = document.createElement("span");
46732         tempEl.appendChild(this.el.dom);
46733         tempEl.innerHTML = "";
46734         this.el.remove();
46735         this.el = null;
46736     },
46737     
46738     /**
46739      * form - if the content panel contains a form - this is a reference to it.
46740      * @type {Roo.form.Form}
46741      */
46742     form : false,
46743     /**
46744      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
46745      *    This contains a reference to it.
46746      * @type {Roo.View}
46747      */
46748     view : false,
46749     
46750       /**
46751      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
46752      * <pre><code>
46753
46754 layout.addxtype({
46755        xtype : 'Form',
46756        items: [ .... ]
46757    }
46758 );
46759
46760 </code></pre>
46761      * @param {Object} cfg Xtype definition of item to add.
46762      */
46763     
46764     addxtype : function(cfg) {
46765         // add form..
46766         if (cfg.xtype.match(/^Form$/)) {
46767             var el = this.el.createChild();
46768
46769             this.form = new  Roo.form.Form(cfg);
46770             
46771             
46772             if ( this.form.allItems.length) this.form.render(el.dom);
46773             return this.form;
46774         }
46775         // should only have one of theses..
46776         if (['View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
46777             // views..
46778             cfg.el = this.el.appendChild(document.createElement("div"));
46779             // factory?
46780             
46781             var ret = new Roo.factory(cfg);
46782             ret.render && ret.render(false, ''); // render blank..
46783             this.view = ret;
46784             return ret;
46785         }
46786         return false;
46787     }
46788 });
46789
46790 /**
46791  * @class Roo.GridPanel
46792  * @extends Roo.ContentPanel
46793  * @constructor
46794  * Create a new GridPanel.
46795  * @param {Roo.grid.Grid} grid The grid for this panel
46796  * @param {String/Object} config A string to set only the panel's title, or a config object
46797  */
46798 Roo.GridPanel = function(grid, config){
46799     
46800   
46801     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
46802         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
46803         
46804     this.wrapper.dom.appendChild(grid.getGridEl().dom);
46805     
46806     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
46807     
46808     if(this.toolbar){
46809         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
46810     }
46811     // xtype created footer. - not sure if will work as we normally have to render first..
46812     if (this.footer && !this.footer.el && this.footer.xtype) {
46813         
46814         this.footer.container = this.grid.getView().getFooterPanel(true);
46815         this.footer.dataSource = this.grid.dataSource;
46816         this.footer = Roo.factory(this.footer, Roo);
46817         
46818     }
46819     
46820     grid.monitorWindowResize = false; // turn off autosizing
46821     grid.autoHeight = false;
46822     grid.autoWidth = false;
46823     this.grid = grid;
46824     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
46825 };
46826
46827 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
46828     getId : function(){
46829         return this.grid.id;
46830     },
46831     
46832     /**
46833      * Returns the grid for this panel
46834      * @return {Roo.grid.Grid} 
46835      */
46836     getGrid : function(){
46837         return this.grid;    
46838     },
46839     
46840     setSize : function(width, height){
46841         if(!this.ignoreResize(width, height)){
46842             var grid = this.grid;
46843             var size = this.adjustForComponents(width, height);
46844             grid.getGridEl().setSize(size.width, size.height);
46845             grid.autoSize();
46846         }
46847     },
46848     
46849     beforeSlide : function(){
46850         this.grid.getView().scroller.clip();
46851     },
46852     
46853     afterSlide : function(){
46854         this.grid.getView().scroller.unclip();
46855     },
46856     
46857     destroy : function(){
46858         this.grid.destroy();
46859         delete this.grid;
46860         Roo.GridPanel.superclass.destroy.call(this); 
46861     }
46862 });
46863
46864
46865 /**
46866  * @class Roo.NestedLayoutPanel
46867  * @extends Roo.ContentPanel
46868  * @constructor
46869  * Create a new NestedLayoutPanel.
46870  * 
46871  * 
46872  * @param {Roo.BorderLayout} layout The layout for this panel
46873  * @param {String/Object} config A string to set only the title or a config object
46874  */
46875 Roo.NestedLayoutPanel = function(layout, config)
46876 {
46877     // construct with only one argument..
46878     /* FIXME - implement nicer consturctors
46879     if (layout.layout) {
46880         config = layout;
46881         layout = config.layout;
46882         delete config.layout;
46883     }
46884     if (layout.xtype && !layout.getEl) {
46885         // then layout needs constructing..
46886         layout = Roo.factory(layout, Roo);
46887     }
46888     */
46889     
46890     
46891     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
46892     
46893     layout.monitorWindowResize = false; // turn off autosizing
46894     this.layout = layout;
46895     this.layout.getEl().addClass("x-layout-nested-layout");
46896     
46897     
46898     
46899     
46900 };
46901
46902 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
46903
46904     setSize : function(width, height){
46905         if(!this.ignoreResize(width, height)){
46906             var size = this.adjustForComponents(width, height);
46907             var el = this.layout.getEl();
46908             el.setSize(size.width, size.height);
46909             var touch = el.dom.offsetWidth;
46910             this.layout.layout();
46911             // ie requires a double layout on the first pass
46912             if(Roo.isIE && !this.initialized){
46913                 this.initialized = true;
46914                 this.layout.layout();
46915             }
46916         }
46917     },
46918     
46919     // activate all subpanels if not currently active..
46920     
46921     setActiveState : function(active){
46922         this.active = active;
46923         if(!active){
46924             this.fireEvent("deactivate", this);
46925             return;
46926         }
46927         
46928         this.fireEvent("activate", this);
46929         // not sure if this should happen before or after..
46930         if (!this.layout) {
46931             return; // should not happen..
46932         }
46933         var reg = false;
46934         for (var r in this.layout.regions) {
46935             reg = this.layout.getRegion(r);
46936             if (reg.getActivePanel()) {
46937                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
46938                 reg.setActivePanel(reg.getActivePanel());
46939                 continue;
46940             }
46941             if (!reg.panels.length) {
46942                 continue;
46943             }
46944             reg.showPanel(reg.getPanel(0));
46945         }
46946         
46947         
46948         
46949         
46950     },
46951     
46952     /**
46953      * Returns the nested BorderLayout for this panel
46954      * @return {Roo.BorderLayout} 
46955      */
46956     getLayout : function(){
46957         return this.layout;
46958     },
46959     
46960      /**
46961      * Adds a xtype elements to the layout of the nested panel
46962      * <pre><code>
46963
46964 panel.addxtype({
46965        xtype : 'ContentPanel',
46966        region: 'west',
46967        items: [ .... ]
46968    }
46969 );
46970
46971 panel.addxtype({
46972         xtype : 'NestedLayoutPanel',
46973         region: 'west',
46974         layout: {
46975            center: { },
46976            west: { }   
46977         },
46978         items : [ ... list of content panels or nested layout panels.. ]
46979    }
46980 );
46981 </code></pre>
46982      * @param {Object} cfg Xtype definition of item to add.
46983      */
46984     addxtype : function(cfg) {
46985         return this.layout.addxtype(cfg);
46986     
46987     }
46988 });
46989
46990 Roo.ScrollPanel = function(el, config, content){
46991     config = config || {};
46992     config.fitToFrame = true;
46993     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
46994     
46995     this.el.dom.style.overflow = "hidden";
46996     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
46997     this.el.removeClass("x-layout-inactive-content");
46998     this.el.on("mousewheel", this.onWheel, this);
46999
47000     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
47001     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
47002     up.unselectable(); down.unselectable();
47003     up.on("click", this.scrollUp, this);
47004     down.on("click", this.scrollDown, this);
47005     up.addClassOnOver("x-scroller-btn-over");
47006     down.addClassOnOver("x-scroller-btn-over");
47007     up.addClassOnClick("x-scroller-btn-click");
47008     down.addClassOnClick("x-scroller-btn-click");
47009     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
47010
47011     this.resizeEl = this.el;
47012     this.el = wrap; this.up = up; this.down = down;
47013 };
47014
47015 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
47016     increment : 100,
47017     wheelIncrement : 5,
47018     scrollUp : function(){
47019         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
47020     },
47021
47022     scrollDown : function(){
47023         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
47024     },
47025
47026     afterScroll : function(){
47027         var el = this.resizeEl;
47028         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
47029         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
47030         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
47031     },
47032
47033     setSize : function(){
47034         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
47035         this.afterScroll();
47036     },
47037
47038     onWheel : function(e){
47039         var d = e.getWheelDelta();
47040         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
47041         this.afterScroll();
47042         e.stopEvent();
47043     },
47044
47045     setContent : function(content, loadScripts){
47046         this.resizeEl.update(content, loadScripts);
47047     }
47048
47049 });
47050
47051
47052
47053
47054
47055
47056
47057
47058
47059 /**
47060  * @class Roo.TreePanel
47061  * @extends Roo.ContentPanel
47062  * @constructor
47063  * Create a new TreePanel. - defaults to fit/scoll contents.
47064  * @param {String/Object} config A string to set only the panel's title, or a config object
47065  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
47066  */
47067 Roo.TreePanel = function(config){
47068     var el = config.el;
47069     var tree = config.tree;
47070     delete config.tree; 
47071     delete config.el; // hopefull!
47072     
47073     // wrapper for IE7 strict & safari scroll issue
47074     
47075     var treeEl = el.createChild();
47076     config.resizeEl = treeEl;
47077     
47078     
47079     
47080     Roo.TreePanel.superclass.constructor.call(this, el, config);
47081  
47082  
47083     this.tree = new Roo.tree.TreePanel(treeEl , tree);
47084     //console.log(tree);
47085     this.on('activate', function()
47086     {
47087         if (this.tree.rendered) {
47088             return;
47089         }
47090         //console.log('render tree');
47091         this.tree.render();
47092     });
47093     
47094     this.on('resize',  function (cp, w, h) {
47095             this.tree.innerCt.setWidth(w);
47096             this.tree.innerCt.setHeight(h);
47097             this.tree.innerCt.setStyle('overflow-y', 'auto');
47098     });
47099
47100         
47101     
47102 };
47103
47104 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
47105     fitToFrame : true,
47106     autoScroll : true
47107 });
47108
47109
47110
47111
47112
47113
47114
47115
47116
47117
47118
47119 /*
47120  * Based on:
47121  * Ext JS Library 1.1.1
47122  * Copyright(c) 2006-2007, Ext JS, LLC.
47123  *
47124  * Originally Released Under LGPL - original licence link has changed is not relivant.
47125  *
47126  * Fork - LGPL
47127  * <script type="text/javascript">
47128  */
47129  
47130
47131 /**
47132  * @class Roo.ReaderLayout
47133  * @extends Roo.BorderLayout
47134  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
47135  * center region containing two nested regions (a top one for a list view and one for item preview below),
47136  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
47137  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
47138  * expedites the setup of the overall layout and regions for this common application style.
47139  * Example:
47140  <pre><code>
47141 var reader = new Roo.ReaderLayout();
47142 var CP = Roo.ContentPanel;  // shortcut for adding
47143
47144 reader.beginUpdate();
47145 reader.add("north", new CP("north", "North"));
47146 reader.add("west", new CP("west", {title: "West"}));
47147 reader.add("east", new CP("east", {title: "East"}));
47148
47149 reader.regions.listView.add(new CP("listView", "List"));
47150 reader.regions.preview.add(new CP("preview", "Preview"));
47151 reader.endUpdate();
47152 </code></pre>
47153 * @constructor
47154 * Create a new ReaderLayout
47155 * @param {Object} config Configuration options
47156 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
47157 * document.body if omitted)
47158 */
47159 Roo.ReaderLayout = function(config, renderTo){
47160     var c = config || {size:{}};
47161     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
47162         north: c.north !== false ? Roo.apply({
47163             split:false,
47164             initialSize: 32,
47165             titlebar: false
47166         }, c.north) : false,
47167         west: c.west !== false ? Roo.apply({
47168             split:true,
47169             initialSize: 200,
47170             minSize: 175,
47171             maxSize: 400,
47172             titlebar: true,
47173             collapsible: true,
47174             animate: true,
47175             margins:{left:5,right:0,bottom:5,top:5},
47176             cmargins:{left:5,right:5,bottom:5,top:5}
47177         }, c.west) : false,
47178         east: c.east !== false ? Roo.apply({
47179             split:true,
47180             initialSize: 200,
47181             minSize: 175,
47182             maxSize: 400,
47183             titlebar: true,
47184             collapsible: true,
47185             animate: true,
47186             margins:{left:0,right:5,bottom:5,top:5},
47187             cmargins:{left:5,right:5,bottom:5,top:5}
47188         }, c.east) : false,
47189         center: Roo.apply({
47190             tabPosition: 'top',
47191             autoScroll:false,
47192             closeOnTab: true,
47193             titlebar:false,
47194             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
47195         }, c.center)
47196     });
47197
47198     this.el.addClass('x-reader');
47199
47200     this.beginUpdate();
47201
47202     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
47203         south: c.preview !== false ? Roo.apply({
47204             split:true,
47205             initialSize: 200,
47206             minSize: 100,
47207             autoScroll:true,
47208             collapsible:true,
47209             titlebar: true,
47210             cmargins:{top:5,left:0, right:0, bottom:0}
47211         }, c.preview) : false,
47212         center: Roo.apply({
47213             autoScroll:false,
47214             titlebar:false,
47215             minHeight:200
47216         }, c.listView)
47217     });
47218     this.add('center', new Roo.NestedLayoutPanel(inner,
47219             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
47220
47221     this.endUpdate();
47222
47223     this.regions.preview = inner.getRegion('south');
47224     this.regions.listView = inner.getRegion('center');
47225 };
47226
47227 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
47228  * Based on:
47229  * Ext JS Library 1.1.1
47230  * Copyright(c) 2006-2007, Ext JS, LLC.
47231  *
47232  * Originally Released Under LGPL - original licence link has changed is not relivant.
47233  *
47234  * Fork - LGPL
47235  * <script type="text/javascript">
47236  */
47237  
47238 /**
47239  * @class Roo.grid.Grid
47240  * @extends Roo.util.Observable
47241  * This class represents the primary interface of a component based grid control.
47242  * <br><br>Usage:<pre><code>
47243  var grid = new Roo.grid.Grid("my-container-id", {
47244      ds: myDataStore,
47245      cm: myColModel,
47246      selModel: mySelectionModel,
47247      autoSizeColumns: true,
47248      monitorWindowResize: false,
47249      trackMouseOver: true
47250  });
47251  // set any options
47252  grid.render();
47253  * </code></pre>
47254  * <b>Common Problems:</b><br/>
47255  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
47256  * element will correct this<br/>
47257  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
47258  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
47259  * are unpredictable.<br/>
47260  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
47261  * grid to calculate dimensions/offsets.<br/>
47262   * @constructor
47263  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
47264  * The container MUST have some type of size defined for the grid to fill. The container will be
47265  * automatically set to position relative if it isn't already.
47266  * @param {Object} config A config object that sets properties on this grid.
47267  */
47268 Roo.grid.Grid = function(container, config){
47269         // initialize the container
47270         this.container = Roo.get(container);
47271         this.container.update("");
47272         this.container.setStyle("overflow", "hidden");
47273     this.container.addClass('x-grid-container');
47274
47275     this.id = this.container.id;
47276
47277     Roo.apply(this, config);
47278     // check and correct shorthanded configs
47279     if(this.ds){
47280         this.dataSource = this.ds;
47281         delete this.ds;
47282     }
47283     if(this.cm){
47284         this.colModel = this.cm;
47285         delete this.cm;
47286     }
47287     if(this.sm){
47288         this.selModel = this.sm;
47289         delete this.sm;
47290     }
47291
47292     if (this.selModel) {
47293         this.selModel = Roo.factory(this.selModel, Roo.grid);
47294         this.sm = this.selModel;
47295         this.sm.xmodule = this.xmodule || false;
47296     }
47297     if (typeof(this.colModel.config) == 'undefined') {
47298         this.colModel = new Roo.grid.ColumnModel(this.colModel);
47299         this.cm = this.colModel;
47300         this.cm.xmodule = this.xmodule || false;
47301     }
47302     if (this.dataSource) {
47303         this.dataSource= Roo.factory(this.dataSource, Roo.data);
47304         this.ds = this.dataSource;
47305         this.ds.xmodule = this.xmodule || false;
47306          
47307     }
47308     
47309     
47310     
47311     if(this.width){
47312         this.container.setWidth(this.width);
47313     }
47314
47315     if(this.height){
47316         this.container.setHeight(this.height);
47317     }
47318     /** @private */
47319         this.addEvents({
47320         // raw events
47321         /**
47322          * @event click
47323          * The raw click event for the entire grid.
47324          * @param {Roo.EventObject} e
47325          */
47326         "click" : true,
47327         /**
47328          * @event dblclick
47329          * The raw dblclick event for the entire grid.
47330          * @param {Roo.EventObject} e
47331          */
47332         "dblclick" : true,
47333         /**
47334          * @event contextmenu
47335          * The raw contextmenu event for the entire grid.
47336          * @param {Roo.EventObject} e
47337          */
47338         "contextmenu" : true,
47339         /**
47340          * @event mousedown
47341          * The raw mousedown event for the entire grid.
47342          * @param {Roo.EventObject} e
47343          */
47344         "mousedown" : true,
47345         /**
47346          * @event mouseup
47347          * The raw mouseup event for the entire grid.
47348          * @param {Roo.EventObject} e
47349          */
47350         "mouseup" : true,
47351         /**
47352          * @event mouseover
47353          * The raw mouseover event for the entire grid.
47354          * @param {Roo.EventObject} e
47355          */
47356         "mouseover" : true,
47357         /**
47358          * @event mouseout
47359          * The raw mouseout event for the entire grid.
47360          * @param {Roo.EventObject} e
47361          */
47362         "mouseout" : true,
47363         /**
47364          * @event keypress
47365          * The raw keypress event for the entire grid.
47366          * @param {Roo.EventObject} e
47367          */
47368         "keypress" : true,
47369         /**
47370          * @event keydown
47371          * The raw keydown event for the entire grid.
47372          * @param {Roo.EventObject} e
47373          */
47374         "keydown" : true,
47375
47376         // custom events
47377
47378         /**
47379          * @event cellclick
47380          * Fires when a cell is clicked
47381          * @param {Grid} this
47382          * @param {Number} rowIndex
47383          * @param {Number} columnIndex
47384          * @param {Roo.EventObject} e
47385          */
47386         "cellclick" : true,
47387         /**
47388          * @event celldblclick
47389          * Fires when a cell is double clicked
47390          * @param {Grid} this
47391          * @param {Number} rowIndex
47392          * @param {Number} columnIndex
47393          * @param {Roo.EventObject} e
47394          */
47395         "celldblclick" : true,
47396         /**
47397          * @event rowclick
47398          * Fires when a row is clicked
47399          * @param {Grid} this
47400          * @param {Number} rowIndex
47401          * @param {Roo.EventObject} e
47402          */
47403         "rowclick" : true,
47404         /**
47405          * @event rowdblclick
47406          * Fires when a row is double clicked
47407          * @param {Grid} this
47408          * @param {Number} rowIndex
47409          * @param {Roo.EventObject} e
47410          */
47411         "rowdblclick" : true,
47412         /**
47413          * @event headerclick
47414          * Fires when a header is clicked
47415          * @param {Grid} this
47416          * @param {Number} columnIndex
47417          * @param {Roo.EventObject} e
47418          */
47419         "headerclick" : true,
47420         /**
47421          * @event headerdblclick
47422          * Fires when a header cell is double clicked
47423          * @param {Grid} this
47424          * @param {Number} columnIndex
47425          * @param {Roo.EventObject} e
47426          */
47427         "headerdblclick" : true,
47428         /**
47429          * @event rowcontextmenu
47430          * Fires when a row is right clicked
47431          * @param {Grid} this
47432          * @param {Number} rowIndex
47433          * @param {Roo.EventObject} e
47434          */
47435         "rowcontextmenu" : true,
47436         /**
47437          * @event cellcontextmenu
47438          * Fires when a cell is right clicked
47439          * @param {Grid} this
47440          * @param {Number} rowIndex
47441          * @param {Number} cellIndex
47442          * @param {Roo.EventObject} e
47443          */
47444          "cellcontextmenu" : true,
47445         /**
47446          * @event headercontextmenu
47447          * Fires when a header is right clicked
47448          * @param {Grid} this
47449          * @param {Number} columnIndex
47450          * @param {Roo.EventObject} e
47451          */
47452         "headercontextmenu" : true,
47453         /**
47454          * @event bodyscroll
47455          * Fires when the body element is scrolled
47456          * @param {Number} scrollLeft
47457          * @param {Number} scrollTop
47458          */
47459         "bodyscroll" : true,
47460         /**
47461          * @event columnresize
47462          * Fires when the user resizes a column
47463          * @param {Number} columnIndex
47464          * @param {Number} newSize
47465          */
47466         "columnresize" : true,
47467         /**
47468          * @event columnmove
47469          * Fires when the user moves a column
47470          * @param {Number} oldIndex
47471          * @param {Number} newIndex
47472          */
47473         "columnmove" : true,
47474         /**
47475          * @event startdrag
47476          * Fires when row(s) start being dragged
47477          * @param {Grid} this
47478          * @param {Roo.GridDD} dd The drag drop object
47479          * @param {event} e The raw browser event
47480          */
47481         "startdrag" : true,
47482         /**
47483          * @event enddrag
47484          * Fires when a drag operation is complete
47485          * @param {Grid} this
47486          * @param {Roo.GridDD} dd The drag drop object
47487          * @param {event} e The raw browser event
47488          */
47489         "enddrag" : true,
47490         /**
47491          * @event dragdrop
47492          * Fires when dragged row(s) are dropped on a valid DD target
47493          * @param {Grid} this
47494          * @param {Roo.GridDD} dd The drag drop object
47495          * @param {String} targetId The target drag drop object
47496          * @param {event} e The raw browser event
47497          */
47498         "dragdrop" : true,
47499         /**
47500          * @event dragover
47501          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
47502          * @param {Grid} this
47503          * @param {Roo.GridDD} dd The drag drop object
47504          * @param {String} targetId The target drag drop object
47505          * @param {event} e The raw browser event
47506          */
47507         "dragover" : true,
47508         /**
47509          * @event dragenter
47510          *  Fires when the dragged row(s) first cross another DD target while being dragged
47511          * @param {Grid} this
47512          * @param {Roo.GridDD} dd The drag drop object
47513          * @param {String} targetId The target drag drop object
47514          * @param {event} e The raw browser event
47515          */
47516         "dragenter" : true,
47517         /**
47518          * @event dragout
47519          * Fires when the dragged row(s) leave another DD target while being dragged
47520          * @param {Grid} this
47521          * @param {Roo.GridDD} dd The drag drop object
47522          * @param {String} targetId The target drag drop object
47523          * @param {event} e The raw browser event
47524          */
47525         "dragout" : true,
47526         /**
47527          * @event rowclass
47528          * Fires when a row is rendered, so you can change add a style to it.
47529          * @param {GridView} gridview   The grid view
47530          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
47531          */
47532         'rowclass' : true,
47533
47534         /**
47535          * @event render
47536          * Fires when the grid is rendered
47537          * @param {Grid} grid
47538          */
47539         'render' : true
47540     });
47541
47542     Roo.grid.Grid.superclass.constructor.call(this);
47543 };
47544 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
47545     
47546     /**
47547      * @cfg {String} ddGroup - drag drop group.
47548      */
47549
47550     /**
47551      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
47552      */
47553     minColumnWidth : 25,
47554
47555     /**
47556      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
47557      * <b>on initial render.</b> It is more efficient to explicitly size the columns
47558      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
47559      */
47560     autoSizeColumns : false,
47561
47562     /**
47563      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
47564      */
47565     autoSizeHeaders : true,
47566
47567     /**
47568      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
47569      */
47570     monitorWindowResize : true,
47571
47572     /**
47573      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
47574      * rows measured to get a columns size. Default is 0 (all rows).
47575      */
47576     maxRowsToMeasure : 0,
47577
47578     /**
47579      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
47580      */
47581     trackMouseOver : true,
47582
47583     /**
47584     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
47585     */
47586     
47587     /**
47588     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
47589     */
47590     enableDragDrop : false,
47591     
47592     /**
47593     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
47594     */
47595     enableColumnMove : true,
47596     
47597     /**
47598     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
47599     */
47600     enableColumnHide : true,
47601     
47602     /**
47603     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
47604     */
47605     enableRowHeightSync : false,
47606     
47607     /**
47608     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
47609     */
47610     stripeRows : true,
47611     
47612     /**
47613     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
47614     */
47615     autoHeight : false,
47616
47617     /**
47618      * @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.
47619      */
47620     autoExpandColumn : false,
47621
47622     /**
47623     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
47624     * Default is 50.
47625     */
47626     autoExpandMin : 50,
47627
47628     /**
47629     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
47630     */
47631     autoExpandMax : 1000,
47632
47633     /**
47634     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
47635     */
47636     view : null,
47637
47638     /**
47639     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
47640     */
47641     loadMask : false,
47642     /**
47643     * @cfg {Roo.dd.DropTarget} dragTarget An {@link Roo.dd.DragTarget} config
47644     */
47645     dropTarget: false,
47646     
47647    
47648     
47649     // private
47650     rendered : false,
47651
47652     /**
47653     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
47654     * of a fixed width. Default is false.
47655     */
47656     /**
47657     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
47658     */
47659     /**
47660      * Called once after all setup has been completed and the grid is ready to be rendered.
47661      * @return {Roo.grid.Grid} this
47662      */
47663     render : function()
47664     {
47665         var c = this.container;
47666         // try to detect autoHeight/width mode
47667         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
47668             this.autoHeight = true;
47669         }
47670         var view = this.getView();
47671         view.init(this);
47672
47673         c.on("click", this.onClick, this);
47674         c.on("dblclick", this.onDblClick, this);
47675         c.on("contextmenu", this.onContextMenu, this);
47676         c.on("keydown", this.onKeyDown, this);
47677
47678         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
47679
47680         this.getSelectionModel().init(this);
47681
47682         view.render();
47683
47684         if(this.loadMask){
47685             this.loadMask = new Roo.LoadMask(this.container,
47686                     Roo.apply({store:this.dataSource}, this.loadMask));
47687         }
47688         
47689         
47690         if (this.toolbar && this.toolbar.xtype) {
47691             this.toolbar.container = this.getView().getHeaderPanel(true);
47692             this.toolbar = new Roo.Toolbar(this.toolbar);
47693         }
47694         if (this.footer && this.footer.xtype) {
47695             this.footer.dataSource = this.getDataSource();
47696             this.footer.container = this.getView().getFooterPanel(true);
47697             this.footer = Roo.factory(this.footer, Roo);
47698         }
47699         if (this.dropTarget && this.dropTarget.xtype) {
47700             delete this.dropTarget.xtype;
47701             this.dropTarget =  new Ext.dd.DropTarget(this.getView().mainBody, this.dropTarget);
47702         }
47703         
47704         
47705         this.rendered = true;
47706         this.fireEvent('render', this);
47707         return this;
47708     },
47709
47710         /**
47711          * Reconfigures the grid to use a different Store and Column Model.
47712          * The View will be bound to the new objects and refreshed.
47713          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
47714          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
47715          */
47716     reconfigure : function(dataSource, colModel){
47717         if(this.loadMask){
47718             this.loadMask.destroy();
47719             this.loadMask = new Roo.LoadMask(this.container,
47720                     Roo.apply({store:dataSource}, this.loadMask));
47721         }
47722         this.view.bind(dataSource, colModel);
47723         this.dataSource = dataSource;
47724         this.colModel = colModel;
47725         this.view.refresh(true);
47726     },
47727
47728     // private
47729     onKeyDown : function(e){
47730         this.fireEvent("keydown", e);
47731     },
47732
47733     /**
47734      * Destroy this grid.
47735      * @param {Boolean} removeEl True to remove the element
47736      */
47737     destroy : function(removeEl, keepListeners){
47738         if(this.loadMask){
47739             this.loadMask.destroy();
47740         }
47741         var c = this.container;
47742         c.removeAllListeners();
47743         this.view.destroy();
47744         this.colModel.purgeListeners();
47745         if(!keepListeners){
47746             this.purgeListeners();
47747         }
47748         c.update("");
47749         if(removeEl === true){
47750             c.remove();
47751         }
47752     },
47753
47754     // private
47755     processEvent : function(name, e){
47756         this.fireEvent(name, e);
47757         var t = e.getTarget();
47758         var v = this.view;
47759         var header = v.findHeaderIndex(t);
47760         if(header !== false){
47761             this.fireEvent("header" + name, this, header, e);
47762         }else{
47763             var row = v.findRowIndex(t);
47764             var cell = v.findCellIndex(t);
47765             if(row !== false){
47766                 this.fireEvent("row" + name, this, row, e);
47767                 if(cell !== false){
47768                     this.fireEvent("cell" + name, this, row, cell, e);
47769                 }
47770             }
47771         }
47772     },
47773
47774     // private
47775     onClick : function(e){
47776         this.processEvent("click", e);
47777     },
47778
47779     // private
47780     onContextMenu : function(e, t){
47781         this.processEvent("contextmenu", e);
47782     },
47783
47784     // private
47785     onDblClick : function(e){
47786         this.processEvent("dblclick", e);
47787     },
47788
47789     // private
47790     walkCells : function(row, col, step, fn, scope){
47791         var cm = this.colModel, clen = cm.getColumnCount();
47792         var ds = this.dataSource, rlen = ds.getCount(), first = true;
47793         if(step < 0){
47794             if(col < 0){
47795                 row--;
47796                 first = false;
47797             }
47798             while(row >= 0){
47799                 if(!first){
47800                     col = clen-1;
47801                 }
47802                 first = false;
47803                 while(col >= 0){
47804                     if(fn.call(scope || this, row, col, cm) === true){
47805                         return [row, col];
47806                     }
47807                     col--;
47808                 }
47809                 row--;
47810             }
47811         } else {
47812             if(col >= clen){
47813                 row++;
47814                 first = false;
47815             }
47816             while(row < rlen){
47817                 if(!first){
47818                     col = 0;
47819                 }
47820                 first = false;
47821                 while(col < clen){
47822                     if(fn.call(scope || this, row, col, cm) === true){
47823                         return [row, col];
47824                     }
47825                     col++;
47826                 }
47827                 row++;
47828             }
47829         }
47830         return null;
47831     },
47832
47833     // private
47834     getSelections : function(){
47835         return this.selModel.getSelections();
47836     },
47837
47838     /**
47839      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
47840      * but if manual update is required this method will initiate it.
47841      */
47842     autoSize : function(){
47843         if(this.rendered){
47844             this.view.layout();
47845             if(this.view.adjustForScroll){
47846                 this.view.adjustForScroll();
47847             }
47848         }
47849     },
47850
47851     /**
47852      * Returns the grid's underlying element.
47853      * @return {Element} The element
47854      */
47855     getGridEl : function(){
47856         return this.container;
47857     },
47858
47859     // private for compatibility, overridden by editor grid
47860     stopEditing : function(){},
47861
47862     /**
47863      * Returns the grid's SelectionModel.
47864      * @return {SelectionModel}
47865      */
47866     getSelectionModel : function(){
47867         if(!this.selModel){
47868             this.selModel = new Roo.grid.RowSelectionModel();
47869         }
47870         return this.selModel;
47871     },
47872
47873     /**
47874      * Returns the grid's DataSource.
47875      * @return {DataSource}
47876      */
47877     getDataSource : function(){
47878         return this.dataSource;
47879     },
47880
47881     /**
47882      * Returns the grid's ColumnModel.
47883      * @return {ColumnModel}
47884      */
47885     getColumnModel : function(){
47886         return this.colModel;
47887     },
47888
47889     /**
47890      * Returns the grid's GridView object.
47891      * @return {GridView}
47892      */
47893     getView : function(){
47894         if(!this.view){
47895             this.view = new Roo.grid.GridView(this.viewConfig);
47896         }
47897         return this.view;
47898     },
47899     /**
47900      * Called to get grid's drag proxy text, by default returns this.ddText.
47901      * @return {String}
47902      */
47903     getDragDropText : function(){
47904         var count = this.selModel.getCount();
47905         return String.format(this.ddText, count, count == 1 ? '' : 's');
47906     }
47907 });
47908 /**
47909  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
47910  * %0 is replaced with the number of selected rows.
47911  * @type String
47912  */
47913 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
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 Roo.grid.AbstractGridView = function(){
47925         this.grid = null;
47926         
47927         this.events = {
47928             "beforerowremoved" : true,
47929             "beforerowsinserted" : true,
47930             "beforerefresh" : true,
47931             "rowremoved" : true,
47932             "rowsinserted" : true,
47933             "rowupdated" : true,
47934             "refresh" : true
47935         };
47936     Roo.grid.AbstractGridView.superclass.constructor.call(this);
47937 };
47938
47939 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
47940     rowClass : "x-grid-row",
47941     cellClass : "x-grid-cell",
47942     tdClass : "x-grid-td",
47943     hdClass : "x-grid-hd",
47944     splitClass : "x-grid-hd-split",
47945     
47946         init: function(grid){
47947         this.grid = grid;
47948                 var cid = this.grid.getGridEl().id;
47949         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
47950         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
47951         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
47952         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
47953         },
47954         
47955         getColumnRenderers : function(){
47956         var renderers = [];
47957         var cm = this.grid.colModel;
47958         var colCount = cm.getColumnCount();
47959         for(var i = 0; i < colCount; i++){
47960             renderers[i] = cm.getRenderer(i);
47961         }
47962         return renderers;
47963     },
47964     
47965     getColumnIds : function(){
47966         var ids = [];
47967         var cm = this.grid.colModel;
47968         var colCount = cm.getColumnCount();
47969         for(var i = 0; i < colCount; i++){
47970             ids[i] = cm.getColumnId(i);
47971         }
47972         return ids;
47973     },
47974     
47975     getDataIndexes : function(){
47976         if(!this.indexMap){
47977             this.indexMap = this.buildIndexMap();
47978         }
47979         return this.indexMap.colToData;
47980     },
47981     
47982     getColumnIndexByDataIndex : function(dataIndex){
47983         if(!this.indexMap){
47984             this.indexMap = this.buildIndexMap();
47985         }
47986         return this.indexMap.dataToCol[dataIndex];
47987     },
47988     
47989     /**
47990      * Set a css style for a column dynamically. 
47991      * @param {Number} colIndex The index of the column
47992      * @param {String} name The css property name
47993      * @param {String} value The css value
47994      */
47995     setCSSStyle : function(colIndex, name, value){
47996         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
47997         Roo.util.CSS.updateRule(selector, name, value);
47998     },
47999     
48000     generateRules : function(cm){
48001         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
48002         Roo.util.CSS.removeStyleSheet(rulesId);
48003         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48004             var cid = cm.getColumnId(i);
48005             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
48006                          this.tdSelector, cid, " {\n}\n",
48007                          this.hdSelector, cid, " {\n}\n",
48008                          this.splitSelector, cid, " {\n}\n");
48009         }
48010         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
48011     }
48012 });/*
48013  * Based on:
48014  * Ext JS Library 1.1.1
48015  * Copyright(c) 2006-2007, Ext JS, LLC.
48016  *
48017  * Originally Released Under LGPL - original licence link has changed is not relivant.
48018  *
48019  * Fork - LGPL
48020  * <script type="text/javascript">
48021  */
48022
48023 // private
48024 // This is a support class used internally by the Grid components
48025 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
48026     this.grid = grid;
48027     this.view = grid.getView();
48028     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
48029     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
48030     if(hd2){
48031         this.setHandleElId(Roo.id(hd));
48032         this.setOuterHandleElId(Roo.id(hd2));
48033     }
48034     this.scroll = false;
48035 };
48036 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
48037     maxDragWidth: 120,
48038     getDragData : function(e){
48039         var t = Roo.lib.Event.getTarget(e);
48040         var h = this.view.findHeaderCell(t);
48041         if(h){
48042             return {ddel: h.firstChild, header:h};
48043         }
48044         return false;
48045     },
48046
48047     onInitDrag : function(e){
48048         this.view.headersDisabled = true;
48049         var clone = this.dragData.ddel.cloneNode(true);
48050         clone.id = Roo.id();
48051         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
48052         this.proxy.update(clone);
48053         return true;
48054     },
48055
48056     afterValidDrop : function(){
48057         var v = this.view;
48058         setTimeout(function(){
48059             v.headersDisabled = false;
48060         }, 50);
48061     },
48062
48063     afterInvalidDrop : function(){
48064         var v = this.view;
48065         setTimeout(function(){
48066             v.headersDisabled = false;
48067         }, 50);
48068     }
48069 });
48070 /*
48071  * Based on:
48072  * Ext JS Library 1.1.1
48073  * Copyright(c) 2006-2007, Ext JS, LLC.
48074  *
48075  * Originally Released Under LGPL - original licence link has changed is not relivant.
48076  *
48077  * Fork - LGPL
48078  * <script type="text/javascript">
48079  */
48080 // private
48081 // This is a support class used internally by the Grid components
48082 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
48083     this.grid = grid;
48084     this.view = grid.getView();
48085     // split the proxies so they don't interfere with mouse events
48086     this.proxyTop = Roo.DomHelper.append(document.body, {
48087         cls:"col-move-top", html:"&#160;"
48088     }, true);
48089     this.proxyBottom = Roo.DomHelper.append(document.body, {
48090         cls:"col-move-bottom", html:"&#160;"
48091     }, true);
48092     this.proxyTop.hide = this.proxyBottom.hide = function(){
48093         this.setLeftTop(-100,-100);
48094         this.setStyle("visibility", "hidden");
48095     };
48096     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
48097     // temporarily disabled
48098     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
48099     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
48100 };
48101 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
48102     proxyOffsets : [-4, -9],
48103     fly: Roo.Element.fly,
48104
48105     getTargetFromEvent : function(e){
48106         var t = Roo.lib.Event.getTarget(e);
48107         var cindex = this.view.findCellIndex(t);
48108         if(cindex !== false){
48109             return this.view.getHeaderCell(cindex);
48110         }
48111         return null;
48112     },
48113
48114     nextVisible : function(h){
48115         var v = this.view, cm = this.grid.colModel;
48116         h = h.nextSibling;
48117         while(h){
48118             if(!cm.isHidden(v.getCellIndex(h))){
48119                 return h;
48120             }
48121             h = h.nextSibling;
48122         }
48123         return null;
48124     },
48125
48126     prevVisible : function(h){
48127         var v = this.view, cm = this.grid.colModel;
48128         h = h.prevSibling;
48129         while(h){
48130             if(!cm.isHidden(v.getCellIndex(h))){
48131                 return h;
48132             }
48133             h = h.prevSibling;
48134         }
48135         return null;
48136     },
48137
48138     positionIndicator : function(h, n, e){
48139         var x = Roo.lib.Event.getPageX(e);
48140         var r = Roo.lib.Dom.getRegion(n.firstChild);
48141         var px, pt, py = r.top + this.proxyOffsets[1];
48142         if((r.right - x) <= (r.right-r.left)/2){
48143             px = r.right+this.view.borderWidth;
48144             pt = "after";
48145         }else{
48146             px = r.left;
48147             pt = "before";
48148         }
48149         var oldIndex = this.view.getCellIndex(h);
48150         var newIndex = this.view.getCellIndex(n);
48151
48152         if(this.grid.colModel.isFixed(newIndex)){
48153             return false;
48154         }
48155
48156         var locked = this.grid.colModel.isLocked(newIndex);
48157
48158         if(pt == "after"){
48159             newIndex++;
48160         }
48161         if(oldIndex < newIndex){
48162             newIndex--;
48163         }
48164         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
48165             return false;
48166         }
48167         px +=  this.proxyOffsets[0];
48168         this.proxyTop.setLeftTop(px, py);
48169         this.proxyTop.show();
48170         if(!this.bottomOffset){
48171             this.bottomOffset = this.view.mainHd.getHeight();
48172         }
48173         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
48174         this.proxyBottom.show();
48175         return pt;
48176     },
48177
48178     onNodeEnter : function(n, dd, e, data){
48179         if(data.header != n){
48180             this.positionIndicator(data.header, n, e);
48181         }
48182     },
48183
48184     onNodeOver : function(n, dd, e, data){
48185         var result = false;
48186         if(data.header != n){
48187             result = this.positionIndicator(data.header, n, e);
48188         }
48189         if(!result){
48190             this.proxyTop.hide();
48191             this.proxyBottom.hide();
48192         }
48193         return result ? this.dropAllowed : this.dropNotAllowed;
48194     },
48195
48196     onNodeOut : function(n, dd, e, data){
48197         this.proxyTop.hide();
48198         this.proxyBottom.hide();
48199     },
48200
48201     onNodeDrop : function(n, dd, e, data){
48202         var h = data.header;
48203         if(h != n){
48204             var cm = this.grid.colModel;
48205             var x = Roo.lib.Event.getPageX(e);
48206             var r = Roo.lib.Dom.getRegion(n.firstChild);
48207             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
48208             var oldIndex = this.view.getCellIndex(h);
48209             var newIndex = this.view.getCellIndex(n);
48210             var locked = cm.isLocked(newIndex);
48211             if(pt == "after"){
48212                 newIndex++;
48213             }
48214             if(oldIndex < newIndex){
48215                 newIndex--;
48216             }
48217             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
48218                 return false;
48219             }
48220             cm.setLocked(oldIndex, locked, true);
48221             cm.moveColumn(oldIndex, newIndex);
48222             this.grid.fireEvent("columnmove", oldIndex, newIndex);
48223             return true;
48224         }
48225         return false;
48226     }
48227 });
48228 /*
48229  * Based on:
48230  * Ext JS Library 1.1.1
48231  * Copyright(c) 2006-2007, Ext JS, LLC.
48232  *
48233  * Originally Released Under LGPL - original licence link has changed is not relivant.
48234  *
48235  * Fork - LGPL
48236  * <script type="text/javascript">
48237  */
48238   
48239 /**
48240  * @class Roo.grid.GridView
48241  * @extends Roo.util.Observable
48242  *
48243  * @constructor
48244  * @param {Object} config
48245  */
48246 Roo.grid.GridView = function(config){
48247     Roo.grid.GridView.superclass.constructor.call(this);
48248     this.el = null;
48249
48250     Roo.apply(this, config);
48251 };
48252
48253 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
48254
48255     /**
48256      * Override this function to apply custom css classes to rows during rendering
48257      * @param {Record} record The record
48258      * @param {Number} index
48259      * @method getRowClass
48260      */
48261     rowClass : "x-grid-row",
48262
48263     cellClass : "x-grid-col",
48264
48265     tdClass : "x-grid-td",
48266
48267     hdClass : "x-grid-hd",
48268
48269     splitClass : "x-grid-split",
48270
48271     sortClasses : ["sort-asc", "sort-desc"],
48272
48273     enableMoveAnim : false,
48274
48275     hlColor: "C3DAF9",
48276
48277     dh : Roo.DomHelper,
48278
48279     fly : Roo.Element.fly,
48280
48281     css : Roo.util.CSS,
48282
48283     borderWidth: 1,
48284
48285     splitOffset: 3,
48286
48287     scrollIncrement : 22,
48288
48289     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
48290
48291     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
48292
48293     bind : function(ds, cm){
48294         if(this.ds){
48295             this.ds.un("load", this.onLoad, this);
48296             this.ds.un("datachanged", this.onDataChange, this);
48297             this.ds.un("add", this.onAdd, this);
48298             this.ds.un("remove", this.onRemove, this);
48299             this.ds.un("update", this.onUpdate, this);
48300             this.ds.un("clear", this.onClear, this);
48301         }
48302         if(ds){
48303             ds.on("load", this.onLoad, this);
48304             ds.on("datachanged", this.onDataChange, this);
48305             ds.on("add", this.onAdd, this);
48306             ds.on("remove", this.onRemove, this);
48307             ds.on("update", this.onUpdate, this);
48308             ds.on("clear", this.onClear, this);
48309         }
48310         this.ds = ds;
48311
48312         if(this.cm){
48313             this.cm.un("widthchange", this.onColWidthChange, this);
48314             this.cm.un("headerchange", this.onHeaderChange, this);
48315             this.cm.un("hiddenchange", this.onHiddenChange, this);
48316             this.cm.un("columnmoved", this.onColumnMove, this);
48317             this.cm.un("columnlockchange", this.onColumnLock, this);
48318         }
48319         if(cm){
48320             this.generateRules(cm);
48321             cm.on("widthchange", this.onColWidthChange, this);
48322             cm.on("headerchange", this.onHeaderChange, this);
48323             cm.on("hiddenchange", this.onHiddenChange, this);
48324             cm.on("columnmoved", this.onColumnMove, this);
48325             cm.on("columnlockchange", this.onColumnLock, this);
48326         }
48327         this.cm = cm;
48328     },
48329
48330     init: function(grid){
48331         Roo.grid.GridView.superclass.init.call(this, grid);
48332
48333         this.bind(grid.dataSource, grid.colModel);
48334
48335         grid.on("headerclick", this.handleHeaderClick, this);
48336
48337         if(grid.trackMouseOver){
48338             grid.on("mouseover", this.onRowOver, this);
48339             grid.on("mouseout", this.onRowOut, this);
48340         }
48341         grid.cancelTextSelection = function(){};
48342         this.gridId = grid.id;
48343
48344         var tpls = this.templates || {};
48345
48346         if(!tpls.master){
48347             tpls.master = new Roo.Template(
48348                '<div class="x-grid" hidefocus="true">',
48349                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
48350                   '<div class="x-grid-topbar"></div>',
48351                   '<div class="x-grid-scroller"><div></div></div>',
48352                   '<div class="x-grid-locked">',
48353                       '<div class="x-grid-header">{lockedHeader}</div>',
48354                       '<div class="x-grid-body">{lockedBody}</div>',
48355                   "</div>",
48356                   '<div class="x-grid-viewport">',
48357                       '<div class="x-grid-header">{header}</div>',
48358                       '<div class="x-grid-body">{body}</div>',
48359                   "</div>",
48360                   '<div class="x-grid-bottombar"></div>',
48361                  
48362                   '<div class="x-grid-resize-proxy">&#160;</div>',
48363                "</div>"
48364             );
48365             tpls.master.disableformats = true;
48366         }
48367
48368         if(!tpls.header){
48369             tpls.header = new Roo.Template(
48370                '<table border="0" cellspacing="0" cellpadding="0">',
48371                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
48372                "</table>{splits}"
48373             );
48374             tpls.header.disableformats = true;
48375         }
48376         tpls.header.compile();
48377
48378         if(!tpls.hcell){
48379             tpls.hcell = new Roo.Template(
48380                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
48381                 '<div class="x-grid-hd-text" unselectable="on">{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
48382                 "</div></td>"
48383              );
48384              tpls.hcell.disableFormats = true;
48385         }
48386         tpls.hcell.compile();
48387
48388         if(!tpls.hsplit){
48389             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style}" unselectable="on">&#160;</div>');
48390             tpls.hsplit.disableFormats = true;
48391         }
48392         tpls.hsplit.compile();
48393
48394         if(!tpls.body){
48395             tpls.body = new Roo.Template(
48396                '<table border="0" cellspacing="0" cellpadding="0">',
48397                "<tbody>{rows}</tbody>",
48398                "</table>"
48399             );
48400             tpls.body.disableFormats = true;
48401         }
48402         tpls.body.compile();
48403
48404         if(!tpls.row){
48405             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
48406             tpls.row.disableFormats = true;
48407         }
48408         tpls.row.compile();
48409
48410         if(!tpls.cell){
48411             tpls.cell = new Roo.Template(
48412                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
48413                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text" unselectable="on" {attr}>{value}</div></div>',
48414                 "</td>"
48415             );
48416             tpls.cell.disableFormats = true;
48417         }
48418         tpls.cell.compile();
48419
48420         this.templates = tpls;
48421     },
48422
48423     // remap these for backwards compat
48424     onColWidthChange : function(){
48425         this.updateColumns.apply(this, arguments);
48426     },
48427     onHeaderChange : function(){
48428         this.updateHeaders.apply(this, arguments);
48429     }, 
48430     onHiddenChange : function(){
48431         this.handleHiddenChange.apply(this, arguments);
48432     },
48433     onColumnMove : function(){
48434         this.handleColumnMove.apply(this, arguments);
48435     },
48436     onColumnLock : function(){
48437         this.handleLockChange.apply(this, arguments);
48438     },
48439
48440     onDataChange : function(){
48441         this.refresh();
48442         this.updateHeaderSortState();
48443     },
48444
48445     onClear : function(){
48446         this.refresh();
48447     },
48448
48449     onUpdate : function(ds, record){
48450         this.refreshRow(record);
48451     },
48452
48453     refreshRow : function(record){
48454         var ds = this.ds, index;
48455         if(typeof record == 'number'){
48456             index = record;
48457             record = ds.getAt(index);
48458         }else{
48459             index = ds.indexOf(record);
48460         }
48461         this.insertRows(ds, index, index, true);
48462         this.onRemove(ds, record, index+1, true);
48463         this.syncRowHeights(index, index);
48464         this.layout();
48465         this.fireEvent("rowupdated", this, index, record);
48466     },
48467
48468     onAdd : function(ds, records, index){
48469         this.insertRows(ds, index, index + (records.length-1));
48470     },
48471
48472     onRemove : function(ds, record, index, isUpdate){
48473         if(isUpdate !== true){
48474             this.fireEvent("beforerowremoved", this, index, record);
48475         }
48476         var bt = this.getBodyTable(), lt = this.getLockedTable();
48477         if(bt.rows[index]){
48478             bt.firstChild.removeChild(bt.rows[index]);
48479         }
48480         if(lt.rows[index]){
48481             lt.firstChild.removeChild(lt.rows[index]);
48482         }
48483         if(isUpdate !== true){
48484             this.stripeRows(index);
48485             this.syncRowHeights(index, index);
48486             this.layout();
48487             this.fireEvent("rowremoved", this, index, record);
48488         }
48489     },
48490
48491     onLoad : function(){
48492         this.scrollToTop();
48493     },
48494
48495     /**
48496      * Scrolls the grid to the top
48497      */
48498     scrollToTop : function(){
48499         if(this.scroller){
48500             this.scroller.dom.scrollTop = 0;
48501             this.syncScroll();
48502         }
48503     },
48504
48505     /**
48506      * Gets a panel in the header of the grid that can be used for toolbars etc.
48507      * After modifying the contents of this panel a call to grid.autoSize() may be
48508      * required to register any changes in size.
48509      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
48510      * @return Roo.Element
48511      */
48512     getHeaderPanel : function(doShow){
48513         if(doShow){
48514             this.headerPanel.show();
48515         }
48516         return this.headerPanel;
48517     },
48518
48519     /**
48520      * Gets a panel in the footer of the grid that can be used for toolbars etc.
48521      * After modifying the contents of this panel a call to grid.autoSize() may be
48522      * required to register any changes in size.
48523      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
48524      * @return Roo.Element
48525      */
48526     getFooterPanel : function(doShow){
48527         if(doShow){
48528             this.footerPanel.show();
48529         }
48530         return this.footerPanel;
48531     },
48532
48533     initElements : function(){
48534         var E = Roo.Element;
48535         var el = this.grid.getGridEl().dom.firstChild;
48536         var cs = el.childNodes;
48537
48538         this.el = new E(el);
48539         
48540          this.focusEl = new E(el.firstChild);
48541         this.focusEl.swallowEvent("click", true);
48542         
48543         this.headerPanel = new E(cs[1]);
48544         this.headerPanel.enableDisplayMode("block");
48545
48546         this.scroller = new E(cs[2]);
48547         this.scrollSizer = new E(this.scroller.dom.firstChild);
48548
48549         this.lockedWrap = new E(cs[3]);
48550         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
48551         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
48552
48553         this.mainWrap = new E(cs[4]);
48554         this.mainHd = new E(this.mainWrap.dom.firstChild);
48555         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
48556
48557         this.footerPanel = new E(cs[5]);
48558         this.footerPanel.enableDisplayMode("block");
48559
48560         this.resizeProxy = new E(cs[6]);
48561
48562         this.headerSelector = String.format(
48563            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
48564            this.lockedHd.id, this.mainHd.id
48565         );
48566
48567         this.splitterSelector = String.format(
48568            '#{0} div.x-grid-split, #{1} div.x-grid-split',
48569            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
48570         );
48571     },
48572     idToCssName : function(s)
48573     {
48574         return s.replace(/[^a-z0-9]+/ig, '-');
48575     },
48576
48577     getHeaderCell : function(index){
48578         return Roo.DomQuery.select(this.headerSelector)[index];
48579     },
48580
48581     getHeaderCellMeasure : function(index){
48582         return this.getHeaderCell(index).firstChild;
48583     },
48584
48585     getHeaderCellText : function(index){
48586         return this.getHeaderCell(index).firstChild.firstChild;
48587     },
48588
48589     getLockedTable : function(){
48590         return this.lockedBody.dom.firstChild;
48591     },
48592
48593     getBodyTable : function(){
48594         return this.mainBody.dom.firstChild;
48595     },
48596
48597     getLockedRow : function(index){
48598         return this.getLockedTable().rows[index];
48599     },
48600
48601     getRow : function(index){
48602         return this.getBodyTable().rows[index];
48603     },
48604
48605     getRowComposite : function(index){
48606         if(!this.rowEl){
48607             this.rowEl = new Roo.CompositeElementLite();
48608         }
48609         var els = [], lrow, mrow;
48610         if(lrow = this.getLockedRow(index)){
48611             els.push(lrow);
48612         }
48613         if(mrow = this.getRow(index)){
48614             els.push(mrow);
48615         }
48616         this.rowEl.elements = els;
48617         return this.rowEl;
48618     },
48619     /**
48620      * Gets the 'td' of the cell
48621      * 
48622      * @param {Integer} rowIndex row to select
48623      * @param {Integer} colIndex column to select
48624      * 
48625      * @return {Object} 
48626      */
48627     getCell : function(rowIndex, colIndex){
48628         var locked = this.cm.getLockedCount();
48629         var source;
48630         if(colIndex < locked){
48631             source = this.lockedBody.dom.firstChild;
48632         }else{
48633             source = this.mainBody.dom.firstChild;
48634             colIndex -= locked;
48635         }
48636         return source.rows[rowIndex].childNodes[colIndex];
48637     },
48638
48639     getCellText : function(rowIndex, colIndex){
48640         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
48641     },
48642
48643     getCellBox : function(cell){
48644         var b = this.fly(cell).getBox();
48645         if(Roo.isOpera){ // opera fails to report the Y
48646             b.y = cell.offsetTop + this.mainBody.getY();
48647         }
48648         return b;
48649     },
48650
48651     getCellIndex : function(cell){
48652         var id = String(cell.className).match(this.cellRE);
48653         if(id){
48654             return parseInt(id[1], 10);
48655         }
48656         return 0;
48657     },
48658
48659     findHeaderIndex : function(n){
48660         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
48661         return r ? this.getCellIndex(r) : false;
48662     },
48663
48664     findHeaderCell : function(n){
48665         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
48666         return r ? r : false;
48667     },
48668
48669     findRowIndex : function(n){
48670         if(!n){
48671             return false;
48672         }
48673         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
48674         return r ? r.rowIndex : false;
48675     },
48676
48677     findCellIndex : function(node){
48678         var stop = this.el.dom;
48679         while(node && node != stop){
48680             if(this.findRE.test(node.className)){
48681                 return this.getCellIndex(node);
48682             }
48683             node = node.parentNode;
48684         }
48685         return false;
48686     },
48687
48688     getColumnId : function(index){
48689         return this.cm.getColumnId(index);
48690     },
48691
48692     getSplitters : function()
48693     {
48694         if(this.splitterSelector){
48695            return Roo.DomQuery.select(this.splitterSelector);
48696         }else{
48697             return null;
48698       }
48699     },
48700
48701     getSplitter : function(index){
48702         return this.getSplitters()[index];
48703     },
48704
48705     onRowOver : function(e, t){
48706         var row;
48707         if((row = this.findRowIndex(t)) !== false){
48708             this.getRowComposite(row).addClass("x-grid-row-over");
48709         }
48710     },
48711
48712     onRowOut : function(e, t){
48713         var row;
48714         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
48715             this.getRowComposite(row).removeClass("x-grid-row-over");
48716         }
48717     },
48718
48719     renderHeaders : function(){
48720         var cm = this.cm;
48721         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
48722         var cb = [], lb = [], sb = [], lsb = [], p = {};
48723         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48724             p.cellId = "x-grid-hd-0-" + i;
48725             p.splitId = "x-grid-csplit-0-" + i;
48726             p.id = cm.getColumnId(i);
48727             p.title = cm.getColumnTooltip(i) || "";
48728             p.value = cm.getColumnHeader(i) || "";
48729             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
48730             if(!cm.isLocked(i)){
48731                 cb[cb.length] = ct.apply(p);
48732                 sb[sb.length] = st.apply(p);
48733             }else{
48734                 lb[lb.length] = ct.apply(p);
48735                 lsb[lsb.length] = st.apply(p);
48736             }
48737         }
48738         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
48739                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
48740     },
48741
48742     updateHeaders : function(){
48743         var html = this.renderHeaders();
48744         this.lockedHd.update(html[0]);
48745         this.mainHd.update(html[1]);
48746     },
48747
48748     /**
48749      * Focuses the specified row.
48750      * @param {Number} row The row index
48751      */
48752     focusRow : function(row)
48753     {
48754         //Roo.log('GridView.focusRow');
48755         var x = this.scroller.dom.scrollLeft;
48756         this.focusCell(row, 0, false);
48757         this.scroller.dom.scrollLeft = x;
48758     },
48759
48760     /**
48761      * Focuses the specified cell.
48762      * @param {Number} row The row index
48763      * @param {Number} col The column index
48764      * @param {Boolean} hscroll false to disable horizontal scrolling
48765      */
48766     focusCell : function(row, col, hscroll)
48767     {
48768         //Roo.log('GridView.focusCell');
48769         var el = this.ensureVisible(row, col, hscroll);
48770         this.focusEl.alignTo(el, "tl-tl");
48771         if(Roo.isGecko){
48772             this.focusEl.focus();
48773         }else{
48774             this.focusEl.focus.defer(1, this.focusEl);
48775         }
48776     },
48777
48778     /**
48779      * Scrolls the specified cell into view
48780      * @param {Number} row The row index
48781      * @param {Number} col The column index
48782      * @param {Boolean} hscroll false to disable horizontal scrolling
48783      */
48784     ensureVisible : function(row, col, hscroll)
48785     {
48786         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
48787         //return null; //disable for testing.
48788         if(typeof row != "number"){
48789             row = row.rowIndex;
48790         }
48791         if(row < 0 && row >= this.ds.getCount()){
48792             return  null;
48793         }
48794         col = (col !== undefined ? col : 0);
48795         var cm = this.grid.colModel;
48796         while(cm.isHidden(col)){
48797             col++;
48798         }
48799
48800         var el = this.getCell(row, col);
48801         if(!el){
48802             return null;
48803         }
48804         var c = this.scroller.dom;
48805
48806         var ctop = parseInt(el.offsetTop, 10);
48807         var cleft = parseInt(el.offsetLeft, 10);
48808         var cbot = ctop + el.offsetHeight;
48809         var cright = cleft + el.offsetWidth;
48810         
48811         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
48812         var stop = parseInt(c.scrollTop, 10);
48813         var sleft = parseInt(c.scrollLeft, 10);
48814         var sbot = stop + ch;
48815         var sright = sleft + c.clientWidth;
48816         /*
48817         Roo.log('GridView.ensureVisible:' +
48818                 ' ctop:' + ctop +
48819                 ' c.clientHeight:' + c.clientHeight +
48820                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
48821                 ' stop:' + stop +
48822                 ' cbot:' + cbot +
48823                 ' sbot:' + sbot +
48824                 ' ch:' + ch  
48825                 );
48826         */
48827         if(ctop < stop){
48828              c.scrollTop = ctop;
48829             //Roo.log("set scrolltop to ctop DISABLE?");
48830         }else if(cbot > sbot){
48831             //Roo.log("set scrolltop to cbot-ch");
48832             c.scrollTop = cbot-ch;
48833         }
48834         
48835         if(hscroll !== false){
48836             if(cleft < sleft){
48837                 c.scrollLeft = cleft;
48838             }else if(cright > sright){
48839                 c.scrollLeft = cright-c.clientWidth;
48840             }
48841         }
48842          
48843         return el;
48844     },
48845
48846     updateColumns : function(){
48847         this.grid.stopEditing();
48848         var cm = this.grid.colModel, colIds = this.getColumnIds();
48849         //var totalWidth = cm.getTotalWidth();
48850         var pos = 0;
48851         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48852             //if(cm.isHidden(i)) continue;
48853             var w = cm.getColumnWidth(i);
48854             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
48855             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
48856         }
48857         this.updateSplitters();
48858     },
48859
48860     generateRules : function(cm){
48861         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
48862         Roo.util.CSS.removeStyleSheet(rulesId);
48863         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48864             var cid = cm.getColumnId(i);
48865             var align = '';
48866             if(cm.config[i].align){
48867                 align = 'text-align:'+cm.config[i].align+';';
48868             }
48869             var hidden = '';
48870             if(cm.isHidden(i)){
48871                 hidden = 'display:none;';
48872             }
48873             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
48874             ruleBuf.push(
48875                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
48876                     this.hdSelector, cid, " {\n", align, width, "}\n",
48877                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
48878                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
48879         }
48880         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
48881     },
48882
48883     updateSplitters : function(){
48884         var cm = this.cm, s = this.getSplitters();
48885         if(s){ // splitters not created yet
48886             var pos = 0, locked = true;
48887             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48888                 if(cm.isHidden(i)) continue;
48889                 var w = cm.getColumnWidth(i); // make sure it's a number
48890                 if(!cm.isLocked(i) && locked){
48891                     pos = 0;
48892                     locked = false;
48893                 }
48894                 pos += w;
48895                 s[i].style.left = (pos-this.splitOffset) + "px";
48896             }
48897         }
48898     },
48899
48900     handleHiddenChange : function(colModel, colIndex, hidden){
48901         if(hidden){
48902             this.hideColumn(colIndex);
48903         }else{
48904             this.unhideColumn(colIndex);
48905         }
48906     },
48907
48908     hideColumn : function(colIndex){
48909         var cid = this.getColumnId(colIndex);
48910         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
48911         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
48912         if(Roo.isSafari){
48913             this.updateHeaders();
48914         }
48915         this.updateSplitters();
48916         this.layout();
48917     },
48918
48919     unhideColumn : function(colIndex){
48920         var cid = this.getColumnId(colIndex);
48921         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
48922         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
48923
48924         if(Roo.isSafari){
48925             this.updateHeaders();
48926         }
48927         this.updateSplitters();
48928         this.layout();
48929     },
48930
48931     insertRows : function(dm, firstRow, lastRow, isUpdate){
48932         if(firstRow == 0 && lastRow == dm.getCount()-1){
48933             this.refresh();
48934         }else{
48935             if(!isUpdate){
48936                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
48937             }
48938             var s = this.getScrollState();
48939             var markup = this.renderRows(firstRow, lastRow);
48940             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
48941             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
48942             this.restoreScroll(s);
48943             if(!isUpdate){
48944                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
48945                 this.syncRowHeights(firstRow, lastRow);
48946                 this.stripeRows(firstRow);
48947                 this.layout();
48948             }
48949         }
48950     },
48951
48952     bufferRows : function(markup, target, index){
48953         var before = null, trows = target.rows, tbody = target.tBodies[0];
48954         if(index < trows.length){
48955             before = trows[index];
48956         }
48957         var b = document.createElement("div");
48958         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
48959         var rows = b.firstChild.rows;
48960         for(var i = 0, len = rows.length; i < len; i++){
48961             if(before){
48962                 tbody.insertBefore(rows[0], before);
48963             }else{
48964                 tbody.appendChild(rows[0]);
48965             }
48966         }
48967         b.innerHTML = "";
48968         b = null;
48969     },
48970
48971     deleteRows : function(dm, firstRow, lastRow){
48972         if(dm.getRowCount()<1){
48973             this.fireEvent("beforerefresh", this);
48974             this.mainBody.update("");
48975             this.lockedBody.update("");
48976             this.fireEvent("refresh", this);
48977         }else{
48978             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
48979             var bt = this.getBodyTable();
48980             var tbody = bt.firstChild;
48981             var rows = bt.rows;
48982             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
48983                 tbody.removeChild(rows[firstRow]);
48984             }
48985             this.stripeRows(firstRow);
48986             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
48987         }
48988     },
48989
48990     updateRows : function(dataSource, firstRow, lastRow){
48991         var s = this.getScrollState();
48992         this.refresh();
48993         this.restoreScroll(s);
48994     },
48995
48996     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
48997         if(!noRefresh){
48998            this.refresh();
48999         }
49000         this.updateHeaderSortState();
49001     },
49002
49003     getScrollState : function(){
49004         
49005         var sb = this.scroller.dom;
49006         return {left: sb.scrollLeft, top: sb.scrollTop};
49007     },
49008
49009     stripeRows : function(startRow){
49010         if(!this.grid.stripeRows || this.ds.getCount() < 1){
49011             return;
49012         }
49013         startRow = startRow || 0;
49014         var rows = this.getBodyTable().rows;
49015         var lrows = this.getLockedTable().rows;
49016         var cls = ' x-grid-row-alt ';
49017         for(var i = startRow, len = rows.length; i < len; i++){
49018             var row = rows[i], lrow = lrows[i];
49019             var isAlt = ((i+1) % 2 == 0);
49020             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
49021             if(isAlt == hasAlt){
49022                 continue;
49023             }
49024             if(isAlt){
49025                 row.className += " x-grid-row-alt";
49026             }else{
49027                 row.className = row.className.replace("x-grid-row-alt", "");
49028             }
49029             if(lrow){
49030                 lrow.className = row.className;
49031             }
49032         }
49033     },
49034
49035     restoreScroll : function(state){
49036         //Roo.log('GridView.restoreScroll');
49037         var sb = this.scroller.dom;
49038         sb.scrollLeft = state.left;
49039         sb.scrollTop = state.top;
49040         this.syncScroll();
49041     },
49042
49043     syncScroll : function(){
49044         //Roo.log('GridView.syncScroll');
49045         var sb = this.scroller.dom;
49046         var sh = this.mainHd.dom;
49047         var bs = this.mainBody.dom;
49048         var lv = this.lockedBody.dom;
49049         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
49050         lv.scrollTop = bs.scrollTop = sb.scrollTop;
49051     },
49052
49053     handleScroll : function(e){
49054         this.syncScroll();
49055         var sb = this.scroller.dom;
49056         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
49057         e.stopEvent();
49058     },
49059
49060     handleWheel : function(e){
49061         var d = e.getWheelDelta();
49062         this.scroller.dom.scrollTop -= d*22;
49063         // set this here to prevent jumpy scrolling on large tables
49064         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
49065         e.stopEvent();
49066     },
49067
49068     renderRows : function(startRow, endRow){
49069         // pull in all the crap needed to render rows
49070         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
49071         var colCount = cm.getColumnCount();
49072
49073         if(ds.getCount() < 1){
49074             return ["", ""];
49075         }
49076
49077         // build a map for all the columns
49078         var cs = [];
49079         for(var i = 0; i < colCount; i++){
49080             var name = cm.getDataIndex(i);
49081             cs[i] = {
49082                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
49083                 renderer : cm.getRenderer(i),
49084                 id : cm.getColumnId(i),
49085                 locked : cm.isLocked(i)
49086             };
49087         }
49088
49089         startRow = startRow || 0;
49090         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
49091
49092         // records to render
49093         var rs = ds.getRange(startRow, endRow);
49094
49095         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
49096     },
49097
49098     // As much as I hate to duplicate code, this was branched because FireFox really hates
49099     // [].join("") on strings. The performance difference was substantial enough to
49100     // branch this function
49101     doRender : Roo.isGecko ?
49102             function(cs, rs, ds, startRow, colCount, stripe){
49103                 var ts = this.templates, ct = ts.cell, rt = ts.row;
49104                 // buffers
49105                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
49106                 
49107                 var hasListener = this.grid.hasListener('rowclass');
49108                 var rowcfg = {};
49109                 for(var j = 0, len = rs.length; j < len; j++){
49110                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
49111                     for(var i = 0; i < colCount; i++){
49112                         c = cs[i];
49113                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
49114                         p.id = c.id;
49115                         p.css = p.attr = "";
49116                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
49117                         if(p.value == undefined || p.value === "") p.value = "&#160;";
49118                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
49119                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
49120                         }
49121                         var markup = ct.apply(p);
49122                         if(!c.locked){
49123                             cb+= markup;
49124                         }else{
49125                             lcb+= markup;
49126                         }
49127                     }
49128                     var alt = [];
49129                     if(stripe && ((rowIndex+1) % 2 == 0)){
49130                         alt.push("x-grid-row-alt")
49131                     }
49132                     if(r.dirty){
49133                         alt.push(  " x-grid-dirty-row");
49134                     }
49135                     rp.cells = lcb;
49136                     if(this.getRowClass){
49137                         alt.push(this.getRowClass(r, rowIndex));
49138                     }
49139                     if (hasListener) {
49140                         rowcfg = {
49141                              
49142                             record: r,
49143                             rowIndex : rowIndex,
49144                             rowClass : ''
49145                         }
49146                         this.grid.fireEvent('rowclass', this, rowcfg);
49147                         alt.push(rowcfg.rowClass);
49148                     }
49149                     rp.alt = alt.join(" ");
49150                     lbuf+= rt.apply(rp);
49151                     rp.cells = cb;
49152                     buf+=  rt.apply(rp);
49153                 }
49154                 return [lbuf, buf];
49155             } :
49156             function(cs, rs, ds, startRow, colCount, stripe){
49157                 var ts = this.templates, ct = ts.cell, rt = ts.row;
49158                 // buffers
49159                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
49160                 var hasListener = this.grid.hasListener('rowclass');
49161                 var rowcfg = {};
49162                 for(var j = 0, len = rs.length; j < len; j++){
49163                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
49164                     for(var i = 0; i < colCount; i++){
49165                         c = cs[i];
49166                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
49167                         p.id = c.id;
49168                         p.css = p.attr = "";
49169                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
49170                         if(p.value == undefined || p.value === "") p.value = "&#160;";
49171                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
49172                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
49173                         }
49174                         var markup = ct.apply(p);
49175                         if(!c.locked){
49176                             cb[cb.length] = markup;
49177                         }else{
49178                             lcb[lcb.length] = markup;
49179                         }
49180                     }
49181                     var alt = [];
49182                     if(stripe && ((rowIndex+1) % 2 == 0)){
49183                         alt.push( "x-grid-row-alt");
49184                     }
49185                     if(r.dirty){
49186                         alt.push(" x-grid-dirty-row");
49187                     }
49188                     rp.cells = lcb;
49189                     if(this.getRowClass){
49190                         alt.push( this.getRowClass(r, rowIndex));
49191                     }
49192                     if (hasListener) {
49193                         rowcfg = {
49194                              
49195                             record: r,
49196                             rowIndex : rowIndex,
49197                             rowClass : ''
49198                         }
49199                         this.grid.fireEvent('rowclass', this, rowcfg);
49200                         alt.push(rowcfg.rowClass);
49201                     }
49202                     rp.alt = alt.join(" ");
49203                     rp.cells = lcb.join("");
49204                     lbuf[lbuf.length] = rt.apply(rp);
49205                     rp.cells = cb.join("");
49206                     buf[buf.length] =  rt.apply(rp);
49207                 }
49208                 return [lbuf.join(""), buf.join("")];
49209             },
49210
49211     renderBody : function(){
49212         var markup = this.renderRows();
49213         var bt = this.templates.body;
49214         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
49215     },
49216
49217     /**
49218      * Refreshes the grid
49219      * @param {Boolean} headersToo
49220      */
49221     refresh : function(headersToo){
49222         this.fireEvent("beforerefresh", this);
49223         this.grid.stopEditing();
49224         var result = this.renderBody();
49225         this.lockedBody.update(result[0]);
49226         this.mainBody.update(result[1]);
49227         if(headersToo === true){
49228             this.updateHeaders();
49229             this.updateColumns();
49230             this.updateSplitters();
49231             this.updateHeaderSortState();
49232         }
49233         this.syncRowHeights();
49234         this.layout();
49235         this.fireEvent("refresh", this);
49236     },
49237
49238     handleColumnMove : function(cm, oldIndex, newIndex){
49239         this.indexMap = null;
49240         var s = this.getScrollState();
49241         this.refresh(true);
49242         this.restoreScroll(s);
49243         this.afterMove(newIndex);
49244     },
49245
49246     afterMove : function(colIndex){
49247         if(this.enableMoveAnim && Roo.enableFx){
49248             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
49249         }
49250         // if multisort - fix sortOrder, and reload..
49251         if (this.grid.dataSource.multiSort) {
49252             // the we can call sort again..
49253             var dm = this.grid.dataSource;
49254             var cm = this.grid.colModel;
49255             var so = [];
49256             for(var i = 0; i < cm.config.length; i++ ) {
49257                 
49258                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
49259                     continue; // dont' bother, it's not in sort list or being set.
49260                 }
49261                 
49262                 so.push(cm.config[i].dataIndex);
49263             };
49264             dm.sortOrder = so;
49265             dm.load(dm.lastOptions);
49266             
49267             
49268         }
49269         
49270     },
49271
49272     updateCell : function(dm, rowIndex, dataIndex){
49273         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
49274         if(typeof colIndex == "undefined"){ // not present in grid
49275             return;
49276         }
49277         var cm = this.grid.colModel;
49278         var cell = this.getCell(rowIndex, colIndex);
49279         var cellText = this.getCellText(rowIndex, colIndex);
49280
49281         var p = {
49282             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
49283             id : cm.getColumnId(colIndex),
49284             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
49285         };
49286         var renderer = cm.getRenderer(colIndex);
49287         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
49288         if(typeof val == "undefined" || val === "") val = "&#160;";
49289         cellText.innerHTML = val;
49290         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
49291         this.syncRowHeights(rowIndex, rowIndex);
49292     },
49293
49294     calcColumnWidth : function(colIndex, maxRowsToMeasure){
49295         var maxWidth = 0;
49296         if(this.grid.autoSizeHeaders){
49297             var h = this.getHeaderCellMeasure(colIndex);
49298             maxWidth = Math.max(maxWidth, h.scrollWidth);
49299         }
49300         var tb, index;
49301         if(this.cm.isLocked(colIndex)){
49302             tb = this.getLockedTable();
49303             index = colIndex;
49304         }else{
49305             tb = this.getBodyTable();
49306             index = colIndex - this.cm.getLockedCount();
49307         }
49308         if(tb && tb.rows){
49309             var rows = tb.rows;
49310             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
49311             for(var i = 0; i < stopIndex; i++){
49312                 var cell = rows[i].childNodes[index].firstChild;
49313                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
49314             }
49315         }
49316         return maxWidth + /*margin for error in IE*/ 5;
49317     },
49318     /**
49319      * Autofit a column to its content.
49320      * @param {Number} colIndex
49321      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
49322      */
49323      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
49324          if(this.cm.isHidden(colIndex)){
49325              return; // can't calc a hidden column
49326          }
49327         if(forceMinSize){
49328             var cid = this.cm.getColumnId(colIndex);
49329             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
49330            if(this.grid.autoSizeHeaders){
49331                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
49332            }
49333         }
49334         var newWidth = this.calcColumnWidth(colIndex);
49335         this.cm.setColumnWidth(colIndex,
49336             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
49337         if(!suppressEvent){
49338             this.grid.fireEvent("columnresize", colIndex, newWidth);
49339         }
49340     },
49341
49342     /**
49343      * Autofits all columns to their content and then expands to fit any extra space in the grid
49344      */
49345      autoSizeColumns : function(){
49346         var cm = this.grid.colModel;
49347         var colCount = cm.getColumnCount();
49348         for(var i = 0; i < colCount; i++){
49349             this.autoSizeColumn(i, true, true);
49350         }
49351         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
49352             this.fitColumns();
49353         }else{
49354             this.updateColumns();
49355             this.layout();
49356         }
49357     },
49358
49359     /**
49360      * Autofits all columns to the grid's width proportionate with their current size
49361      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
49362      */
49363     fitColumns : function(reserveScrollSpace){
49364         var cm = this.grid.colModel;
49365         var colCount = cm.getColumnCount();
49366         var cols = [];
49367         var width = 0;
49368         var i, w;
49369         for (i = 0; i < colCount; i++){
49370             if(!cm.isHidden(i) && !cm.isFixed(i)){
49371                 w = cm.getColumnWidth(i);
49372                 cols.push(i);
49373                 cols.push(w);
49374                 width += w;
49375             }
49376         }
49377         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
49378         if(reserveScrollSpace){
49379             avail -= 17;
49380         }
49381         var frac = (avail - cm.getTotalWidth())/width;
49382         while (cols.length){
49383             w = cols.pop();
49384             i = cols.pop();
49385             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
49386         }
49387         this.updateColumns();
49388         this.layout();
49389     },
49390
49391     onRowSelect : function(rowIndex){
49392         var row = this.getRowComposite(rowIndex);
49393         row.addClass("x-grid-row-selected");
49394     },
49395
49396     onRowDeselect : function(rowIndex){
49397         var row = this.getRowComposite(rowIndex);
49398         row.removeClass("x-grid-row-selected");
49399     },
49400
49401     onCellSelect : function(row, col){
49402         var cell = this.getCell(row, col);
49403         if(cell){
49404             Roo.fly(cell).addClass("x-grid-cell-selected");
49405         }
49406     },
49407
49408     onCellDeselect : function(row, col){
49409         var cell = this.getCell(row, col);
49410         if(cell){
49411             Roo.fly(cell).removeClass("x-grid-cell-selected");
49412         }
49413     },
49414
49415     updateHeaderSortState : function(){
49416         
49417         // sort state can be single { field: xxx, direction : yyy}
49418         // or   { xxx=>ASC , yyy : DESC ..... }
49419         
49420         var mstate = {};
49421         if (!this.ds.multiSort) { 
49422             var state = this.ds.getSortState();
49423             if(!state){
49424                 return;
49425             }
49426             mstate[state.field] = state.direction;
49427             // FIXME... - this is not used here.. but might be elsewhere..
49428             this.sortState = state;
49429             
49430         } else {
49431             mstate = this.ds.sortToggle;
49432         }
49433         //remove existing sort classes..
49434         
49435         var sc = this.sortClasses;
49436         var hds = this.el.select(this.headerSelector).removeClass(sc);
49437         
49438         for(var f in mstate) {
49439         
49440             var sortColumn = this.cm.findColumnIndex(f);
49441             
49442             if(sortColumn != -1){
49443                 var sortDir = mstate[f];        
49444                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
49445             }
49446         }
49447         
49448          
49449         
49450     },
49451
49452
49453     handleHeaderClick : function(g, index){
49454         if(this.headersDisabled){
49455             return;
49456         }
49457         var dm = g.dataSource, cm = g.colModel;
49458         if(!cm.isSortable(index)){
49459             return;
49460         }
49461         g.stopEditing();
49462         
49463         if (dm.multiSort) {
49464             // update the sortOrder
49465             var so = [];
49466             for(var i = 0; i < cm.config.length; i++ ) {
49467                 
49468                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
49469                     continue; // dont' bother, it's not in sort list or being set.
49470                 }
49471                 
49472                 so.push(cm.config[i].dataIndex);
49473             };
49474             dm.sortOrder = so;
49475         }
49476         
49477         
49478         dm.sort(cm.getDataIndex(index));
49479     },
49480
49481
49482     destroy : function(){
49483         if(this.colMenu){
49484             this.colMenu.removeAll();
49485             Roo.menu.MenuMgr.unregister(this.colMenu);
49486             this.colMenu.getEl().remove();
49487             delete this.colMenu;
49488         }
49489         if(this.hmenu){
49490             this.hmenu.removeAll();
49491             Roo.menu.MenuMgr.unregister(this.hmenu);
49492             this.hmenu.getEl().remove();
49493             delete this.hmenu;
49494         }
49495         if(this.grid.enableColumnMove){
49496             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
49497             if(dds){
49498                 for(var dd in dds){
49499                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
49500                         var elid = dds[dd].dragElId;
49501                         dds[dd].unreg();
49502                         Roo.get(elid).remove();
49503                     } else if(dds[dd].config.isTarget){
49504                         dds[dd].proxyTop.remove();
49505                         dds[dd].proxyBottom.remove();
49506                         dds[dd].unreg();
49507                     }
49508                     if(Roo.dd.DDM.locationCache[dd]){
49509                         delete Roo.dd.DDM.locationCache[dd];
49510                     }
49511                 }
49512                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
49513             }
49514         }
49515         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
49516         this.bind(null, null);
49517         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
49518     },
49519
49520     handleLockChange : function(){
49521         this.refresh(true);
49522     },
49523
49524     onDenyColumnLock : function(){
49525
49526     },
49527
49528     onDenyColumnHide : function(){
49529
49530     },
49531
49532     handleHdMenuClick : function(item){
49533         var index = this.hdCtxIndex;
49534         var cm = this.cm, ds = this.ds;
49535         switch(item.id){
49536             case "asc":
49537                 ds.sort(cm.getDataIndex(index), "ASC");
49538                 break;
49539             case "desc":
49540                 ds.sort(cm.getDataIndex(index), "DESC");
49541                 break;
49542             case "lock":
49543                 var lc = cm.getLockedCount();
49544                 if(cm.getColumnCount(true) <= lc+1){
49545                     this.onDenyColumnLock();
49546                     return;
49547                 }
49548                 if(lc != index){
49549                     cm.setLocked(index, true, true);
49550                     cm.moveColumn(index, lc);
49551                     this.grid.fireEvent("columnmove", index, lc);
49552                 }else{
49553                     cm.setLocked(index, true);
49554                 }
49555             break;
49556             case "unlock":
49557                 var lc = cm.getLockedCount();
49558                 if((lc-1) != index){
49559                     cm.setLocked(index, false, true);
49560                     cm.moveColumn(index, lc-1);
49561                     this.grid.fireEvent("columnmove", index, lc-1);
49562                 }else{
49563                     cm.setLocked(index, false);
49564                 }
49565             break;
49566             default:
49567                 index = cm.getIndexById(item.id.substr(4));
49568                 if(index != -1){
49569                     if(item.checked && cm.getColumnCount(true) <= 1){
49570                         this.onDenyColumnHide();
49571                         return false;
49572                     }
49573                     cm.setHidden(index, item.checked);
49574                 }
49575         }
49576         return true;
49577     },
49578
49579     beforeColMenuShow : function(){
49580         var cm = this.cm,  colCount = cm.getColumnCount();
49581         this.colMenu.removeAll();
49582         for(var i = 0; i < colCount; i++){
49583             this.colMenu.add(new Roo.menu.CheckItem({
49584                 id: "col-"+cm.getColumnId(i),
49585                 text: cm.getColumnHeader(i),
49586                 checked: !cm.isHidden(i),
49587                 hideOnClick:false
49588             }));
49589         }
49590     },
49591
49592     handleHdCtx : function(g, index, e){
49593         e.stopEvent();
49594         var hd = this.getHeaderCell(index);
49595         this.hdCtxIndex = index;
49596         var ms = this.hmenu.items, cm = this.cm;
49597         ms.get("asc").setDisabled(!cm.isSortable(index));
49598         ms.get("desc").setDisabled(!cm.isSortable(index));
49599         if(this.grid.enableColLock !== false){
49600             ms.get("lock").setDisabled(cm.isLocked(index));
49601             ms.get("unlock").setDisabled(!cm.isLocked(index));
49602         }
49603         this.hmenu.show(hd, "tl-bl");
49604     },
49605
49606     handleHdOver : function(e){
49607         var hd = this.findHeaderCell(e.getTarget());
49608         if(hd && !this.headersDisabled){
49609             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
49610                this.fly(hd).addClass("x-grid-hd-over");
49611             }
49612         }
49613     },
49614
49615     handleHdOut : function(e){
49616         var hd = this.findHeaderCell(e.getTarget());
49617         if(hd){
49618             this.fly(hd).removeClass("x-grid-hd-over");
49619         }
49620     },
49621
49622     handleSplitDblClick : function(e, t){
49623         var i = this.getCellIndex(t);
49624         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
49625             this.autoSizeColumn(i, true);
49626             this.layout();
49627         }
49628     },
49629
49630     render : function(){
49631
49632         var cm = this.cm;
49633         var colCount = cm.getColumnCount();
49634
49635         if(this.grid.monitorWindowResize === true){
49636             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
49637         }
49638         var header = this.renderHeaders();
49639         var body = this.templates.body.apply({rows:""});
49640         var html = this.templates.master.apply({
49641             lockedBody: body,
49642             body: body,
49643             lockedHeader: header[0],
49644             header: header[1]
49645         });
49646
49647         //this.updateColumns();
49648
49649         this.grid.getGridEl().dom.innerHTML = html;
49650
49651         this.initElements();
49652         
49653         // a kludge to fix the random scolling effect in webkit
49654         this.el.on("scroll", function() {
49655             this.el.dom.scrollTop=0; // hopefully not recursive..
49656         },this);
49657
49658         this.scroller.on("scroll", this.handleScroll, this);
49659         this.lockedBody.on("mousewheel", this.handleWheel, this);
49660         this.mainBody.on("mousewheel", this.handleWheel, this);
49661
49662         this.mainHd.on("mouseover", this.handleHdOver, this);
49663         this.mainHd.on("mouseout", this.handleHdOut, this);
49664         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
49665                 {delegate: "."+this.splitClass});
49666
49667         this.lockedHd.on("mouseover", this.handleHdOver, this);
49668         this.lockedHd.on("mouseout", this.handleHdOut, this);
49669         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
49670                 {delegate: "."+this.splitClass});
49671
49672         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
49673             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
49674         }
49675
49676         this.updateSplitters();
49677
49678         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
49679             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
49680             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
49681         }
49682
49683         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
49684             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
49685             this.hmenu.add(
49686                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
49687                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
49688             );
49689             if(this.grid.enableColLock !== false){
49690                 this.hmenu.add('-',
49691                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
49692                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
49693                 );
49694             }
49695             if(this.grid.enableColumnHide !== false){
49696
49697                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
49698                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
49699                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
49700
49701                 this.hmenu.add('-',
49702                     {id:"columns", text: this.columnsText, menu: this.colMenu}
49703                 );
49704             }
49705             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
49706
49707             this.grid.on("headercontextmenu", this.handleHdCtx, this);
49708         }
49709
49710         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
49711             this.dd = new Roo.grid.GridDragZone(this.grid, {
49712                 ddGroup : this.grid.ddGroup || 'GridDD'
49713             });
49714         }
49715
49716         /*
49717         for(var i = 0; i < colCount; i++){
49718             if(cm.isHidden(i)){
49719                 this.hideColumn(i);
49720             }
49721             if(cm.config[i].align){
49722                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
49723                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
49724             }
49725         }*/
49726         
49727         this.updateHeaderSortState();
49728
49729         this.beforeInitialResize();
49730         this.layout(true);
49731
49732         // two part rendering gives faster view to the user
49733         this.renderPhase2.defer(1, this);
49734     },
49735
49736     renderPhase2 : function(){
49737         // render the rows now
49738         this.refresh();
49739         if(this.grid.autoSizeColumns){
49740             this.autoSizeColumns();
49741         }
49742     },
49743
49744     beforeInitialResize : function(){
49745
49746     },
49747
49748     onColumnSplitterMoved : function(i, w){
49749         this.userResized = true;
49750         var cm = this.grid.colModel;
49751         cm.setColumnWidth(i, w, true);
49752         var cid = cm.getColumnId(i);
49753         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
49754         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
49755         this.updateSplitters();
49756         this.layout();
49757         this.grid.fireEvent("columnresize", i, w);
49758     },
49759
49760     syncRowHeights : function(startIndex, endIndex){
49761         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
49762             startIndex = startIndex || 0;
49763             var mrows = this.getBodyTable().rows;
49764             var lrows = this.getLockedTable().rows;
49765             var len = mrows.length-1;
49766             endIndex = Math.min(endIndex || len, len);
49767             for(var i = startIndex; i <= endIndex; i++){
49768                 var m = mrows[i], l = lrows[i];
49769                 var h = Math.max(m.offsetHeight, l.offsetHeight);
49770                 m.style.height = l.style.height = h + "px";
49771             }
49772         }
49773     },
49774
49775     layout : function(initialRender, is2ndPass){
49776         var g = this.grid;
49777         var auto = g.autoHeight;
49778         var scrollOffset = 16;
49779         var c = g.getGridEl(), cm = this.cm,
49780                 expandCol = g.autoExpandColumn,
49781                 gv = this;
49782         //c.beginMeasure();
49783
49784         if(!c.dom.offsetWidth){ // display:none?
49785             if(initialRender){
49786                 this.lockedWrap.show();
49787                 this.mainWrap.show();
49788             }
49789             return;
49790         }
49791
49792         var hasLock = this.cm.isLocked(0);
49793
49794         var tbh = this.headerPanel.getHeight();
49795         var bbh = this.footerPanel.getHeight();
49796
49797         if(auto){
49798             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
49799             var newHeight = ch + c.getBorderWidth("tb");
49800             if(g.maxHeight){
49801                 newHeight = Math.min(g.maxHeight, newHeight);
49802             }
49803             c.setHeight(newHeight);
49804         }
49805
49806         if(g.autoWidth){
49807             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
49808         }
49809
49810         var s = this.scroller;
49811
49812         var csize = c.getSize(true);
49813
49814         this.el.setSize(csize.width, csize.height);
49815
49816         this.headerPanel.setWidth(csize.width);
49817         this.footerPanel.setWidth(csize.width);
49818
49819         var hdHeight = this.mainHd.getHeight();
49820         var vw = csize.width;
49821         var vh = csize.height - (tbh + bbh);
49822
49823         s.setSize(vw, vh);
49824
49825         var bt = this.getBodyTable();
49826         var ltWidth = hasLock ?
49827                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
49828
49829         var scrollHeight = bt.offsetHeight;
49830         var scrollWidth = ltWidth + bt.offsetWidth;
49831         var vscroll = false, hscroll = false;
49832
49833         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
49834
49835         var lw = this.lockedWrap, mw = this.mainWrap;
49836         var lb = this.lockedBody, mb = this.mainBody;
49837
49838         setTimeout(function(){
49839             var t = s.dom.offsetTop;
49840             var w = s.dom.clientWidth,
49841                 h = s.dom.clientHeight;
49842
49843             lw.setTop(t);
49844             lw.setSize(ltWidth, h);
49845
49846             mw.setLeftTop(ltWidth, t);
49847             mw.setSize(w-ltWidth, h);
49848
49849             lb.setHeight(h-hdHeight);
49850             mb.setHeight(h-hdHeight);
49851
49852             if(is2ndPass !== true && !gv.userResized && expandCol){
49853                 // high speed resize without full column calculation
49854                 
49855                 var ci = cm.getIndexById(expandCol);
49856                 if (ci < 0) {
49857                     ci = cm.findColumnIndex(expandCol);
49858                 }
49859                 ci = Math.max(0, ci); // make sure it's got at least the first col.
49860                 var expandId = cm.getColumnId(ci);
49861                 var  tw = cm.getTotalWidth(false);
49862                 var currentWidth = cm.getColumnWidth(ci);
49863                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
49864                 if(currentWidth != cw){
49865                     cm.setColumnWidth(ci, cw, true);
49866                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
49867                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
49868                     gv.updateSplitters();
49869                     gv.layout(false, true);
49870                 }
49871             }
49872
49873             if(initialRender){
49874                 lw.show();
49875                 mw.show();
49876             }
49877             //c.endMeasure();
49878         }, 10);
49879     },
49880
49881     onWindowResize : function(){
49882         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
49883             return;
49884         }
49885         this.layout();
49886     },
49887
49888     appendFooter : function(parentEl){
49889         return null;
49890     },
49891
49892     sortAscText : "Sort Ascending",
49893     sortDescText : "Sort Descending",
49894     lockText : "Lock Column",
49895     unlockText : "Unlock Column",
49896     columnsText : "Columns"
49897 });
49898
49899
49900 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
49901     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
49902     this.proxy.el.addClass('x-grid3-col-dd');
49903 };
49904
49905 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
49906     handleMouseDown : function(e){
49907
49908     },
49909
49910     callHandleMouseDown : function(e){
49911         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
49912     }
49913 });
49914 /*
49915  * Based on:
49916  * Ext JS Library 1.1.1
49917  * Copyright(c) 2006-2007, Ext JS, LLC.
49918  *
49919  * Originally Released Under LGPL - original licence link has changed is not relivant.
49920  *
49921  * Fork - LGPL
49922  * <script type="text/javascript">
49923  */
49924  
49925 // private
49926 // This is a support class used internally by the Grid components
49927 Roo.grid.SplitDragZone = function(grid, hd, hd2){
49928     this.grid = grid;
49929     this.view = grid.getView();
49930     this.proxy = this.view.resizeProxy;
49931     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
49932         "gridSplitters" + this.grid.getGridEl().id, {
49933         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
49934     });
49935     this.setHandleElId(Roo.id(hd));
49936     this.setOuterHandleElId(Roo.id(hd2));
49937     this.scroll = false;
49938 };
49939 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
49940     fly: Roo.Element.fly,
49941
49942     b4StartDrag : function(x, y){
49943         this.view.headersDisabled = true;
49944         this.proxy.setHeight(this.view.mainWrap.getHeight());
49945         var w = this.cm.getColumnWidth(this.cellIndex);
49946         var minw = Math.max(w-this.grid.minColumnWidth, 0);
49947         this.resetConstraints();
49948         this.setXConstraint(minw, 1000);
49949         this.setYConstraint(0, 0);
49950         this.minX = x - minw;
49951         this.maxX = x + 1000;
49952         this.startPos = x;
49953         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
49954     },
49955
49956
49957     handleMouseDown : function(e){
49958         ev = Roo.EventObject.setEvent(e);
49959         var t = this.fly(ev.getTarget());
49960         if(t.hasClass("x-grid-split")){
49961             this.cellIndex = this.view.getCellIndex(t.dom);
49962             this.split = t.dom;
49963             this.cm = this.grid.colModel;
49964             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
49965                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
49966             }
49967         }
49968     },
49969
49970     endDrag : function(e){
49971         this.view.headersDisabled = false;
49972         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
49973         var diff = endX - this.startPos;
49974         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
49975     },
49976
49977     autoOffset : function(){
49978         this.setDelta(0,0);
49979     }
49980 });/*
49981  * Based on:
49982  * Ext JS Library 1.1.1
49983  * Copyright(c) 2006-2007, Ext JS, LLC.
49984  *
49985  * Originally Released Under LGPL - original licence link has changed is not relivant.
49986  *
49987  * Fork - LGPL
49988  * <script type="text/javascript">
49989  */
49990  
49991 // private
49992 // This is a support class used internally by the Grid components
49993 Roo.grid.GridDragZone = function(grid, config){
49994     this.view = grid.getView();
49995     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
49996     if(this.view.lockedBody){
49997         this.setHandleElId(Roo.id(this.view.mainBody.dom));
49998         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
49999     }
50000     this.scroll = false;
50001     this.grid = grid;
50002     this.ddel = document.createElement('div');
50003     this.ddel.className = 'x-grid-dd-wrap';
50004 };
50005
50006 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
50007     ddGroup : "GridDD",
50008
50009     getDragData : function(e){
50010         var t = Roo.lib.Event.getTarget(e);
50011         var rowIndex = this.view.findRowIndex(t);
50012         if(rowIndex !== false){
50013             var sm = this.grid.selModel;
50014             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
50015               //  sm.mouseDown(e, t);
50016             //}
50017             if (e.hasModifier()){
50018                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
50019             }
50020             return {grid: this.grid, ddel: this.ddel, rowIndex: rowIndex, selections:sm.getSelections()};
50021         }
50022         return false;
50023     },
50024
50025     onInitDrag : function(e){
50026         var data = this.dragData;
50027         this.ddel.innerHTML = this.grid.getDragDropText();
50028         this.proxy.update(this.ddel);
50029         // fire start drag?
50030     },
50031
50032     afterRepair : function(){
50033         this.dragging = false;
50034     },
50035
50036     getRepairXY : function(e, data){
50037         return false;
50038     },
50039
50040     onEndDrag : function(data, e){
50041         // fire end drag?
50042     },
50043
50044     onValidDrop : function(dd, e, id){
50045         // fire drag drop?
50046         this.hideProxy();
50047     },
50048
50049     beforeInvalidDrop : function(e, id){
50050
50051     }
50052 });/*
50053  * Based on:
50054  * Ext JS Library 1.1.1
50055  * Copyright(c) 2006-2007, Ext JS, LLC.
50056  *
50057  * Originally Released Under LGPL - original licence link has changed is not relivant.
50058  *
50059  * Fork - LGPL
50060  * <script type="text/javascript">
50061  */
50062  
50063
50064 /**
50065  * @class Roo.grid.ColumnModel
50066  * @extends Roo.util.Observable
50067  * This is the default implementation of a ColumnModel used by the Grid. It defines
50068  * the columns in the grid.
50069  * <br>Usage:<br>
50070  <pre><code>
50071  var colModel = new Roo.grid.ColumnModel([
50072         {header: "Ticker", width: 60, sortable: true, locked: true},
50073         {header: "Company Name", width: 150, sortable: true},
50074         {header: "Market Cap.", width: 100, sortable: true},
50075         {header: "$ Sales", width: 100, sortable: true, renderer: money},
50076         {header: "Employees", width: 100, sortable: true, resizable: false}
50077  ]);
50078  </code></pre>
50079  * <p>
50080  
50081  * The config options listed for this class are options which may appear in each
50082  * individual column definition.
50083  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
50084  * @constructor
50085  * @param {Object} config An Array of column config objects. See this class's
50086  * config objects for details.
50087 */
50088 Roo.grid.ColumnModel = function(config){
50089         /**
50090      * The config passed into the constructor
50091      */
50092     this.config = config;
50093     this.lookup = {};
50094
50095     // if no id, create one
50096     // if the column does not have a dataIndex mapping,
50097     // map it to the order it is in the config
50098     for(var i = 0, len = config.length; i < len; i++){
50099         var c = config[i];
50100         if(typeof c.dataIndex == "undefined"){
50101             c.dataIndex = i;
50102         }
50103         if(typeof c.renderer == "string"){
50104             c.renderer = Roo.util.Format[c.renderer];
50105         }
50106         if(typeof c.id == "undefined"){
50107             c.id = Roo.id();
50108         }
50109         if(c.editor && c.editor.xtype){
50110             c.editor  = Roo.factory(c.editor, Roo.grid);
50111         }
50112         if(c.editor && c.editor.isFormField){
50113             c.editor = new Roo.grid.GridEditor(c.editor);
50114         }
50115         this.lookup[c.id] = c;
50116     }
50117
50118     /**
50119      * The width of columns which have no width specified (defaults to 100)
50120      * @type Number
50121      */
50122     this.defaultWidth = 100;
50123
50124     /**
50125      * Default sortable of columns which have no sortable specified (defaults to false)
50126      * @type Boolean
50127      */
50128     this.defaultSortable = false;
50129
50130     this.addEvents({
50131         /**
50132              * @event widthchange
50133              * Fires when the width of a column changes.
50134              * @param {ColumnModel} this
50135              * @param {Number} columnIndex The column index
50136              * @param {Number} newWidth The new width
50137              */
50138             "widthchange": true,
50139         /**
50140              * @event headerchange
50141              * Fires when the text of a header changes.
50142              * @param {ColumnModel} this
50143              * @param {Number} columnIndex The column index
50144              * @param {Number} newText The new header text
50145              */
50146             "headerchange": true,
50147         /**
50148              * @event hiddenchange
50149              * Fires when a column is hidden or "unhidden".
50150              * @param {ColumnModel} this
50151              * @param {Number} columnIndex The column index
50152              * @param {Boolean} hidden true if hidden, false otherwise
50153              */
50154             "hiddenchange": true,
50155             /**
50156          * @event columnmoved
50157          * Fires when a column is moved.
50158          * @param {ColumnModel} this
50159          * @param {Number} oldIndex
50160          * @param {Number} newIndex
50161          */
50162         "columnmoved" : true,
50163         /**
50164          * @event columlockchange
50165          * Fires when a column's locked state is changed
50166          * @param {ColumnModel} this
50167          * @param {Number} colIndex
50168          * @param {Boolean} locked true if locked
50169          */
50170         "columnlockchange" : true
50171     });
50172     Roo.grid.ColumnModel.superclass.constructor.call(this);
50173 };
50174 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
50175     /**
50176      * @cfg {String} header The header text to display in the Grid view.
50177      */
50178     /**
50179      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
50180      * {@link Roo.data.Record} definition from which to draw the column's value. If not
50181      * specified, the column's index is used as an index into the Record's data Array.
50182      */
50183     /**
50184      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
50185      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
50186      */
50187     /**
50188      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
50189      * Defaults to the value of the {@link #defaultSortable} property.
50190      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
50191      */
50192     /**
50193      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
50194      */
50195     /**
50196      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
50197      */
50198     /**
50199      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
50200      */
50201     /**
50202      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
50203      */
50204     /**
50205      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
50206      * given the cell's data value. See {@link #setRenderer}. If not specified, the
50207      * default renderer uses the raw data value.
50208      */
50209        /**
50210      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
50211      */
50212     /**
50213      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
50214      */
50215
50216     /**
50217      * Returns the id of the column at the specified index.
50218      * @param {Number} index The column index
50219      * @return {String} the id
50220      */
50221     getColumnId : function(index){
50222         return this.config[index].id;
50223     },
50224
50225     /**
50226      * Returns the column for a specified id.
50227      * @param {String} id The column id
50228      * @return {Object} the column
50229      */
50230     getColumnById : function(id){
50231         return this.lookup[id];
50232     },
50233
50234     
50235     /**
50236      * Returns the column for a specified dataIndex.
50237      * @param {String} dataIndex The column dataIndex
50238      * @return {Object|Boolean} the column or false if not found
50239      */
50240     getColumnByDataIndex: function(dataIndex){
50241         var index = this.findColumnIndex(dataIndex);
50242         return index > -1 ? this.config[index] : false;
50243     },
50244     
50245     /**
50246      * Returns the index for a specified column id.
50247      * @param {String} id The column id
50248      * @return {Number} the index, or -1 if not found
50249      */
50250     getIndexById : function(id){
50251         for(var i = 0, len = this.config.length; i < len; i++){
50252             if(this.config[i].id == id){
50253                 return i;
50254             }
50255         }
50256         return -1;
50257     },
50258     
50259     /**
50260      * Returns the index for a specified column dataIndex.
50261      * @param {String} dataIndex The column dataIndex
50262      * @return {Number} the index, or -1 if not found
50263      */
50264     
50265     findColumnIndex : function(dataIndex){
50266         for(var i = 0, len = this.config.length; i < len; i++){
50267             if(this.config[i].dataIndex == dataIndex){
50268                 return i;
50269             }
50270         }
50271         return -1;
50272     },
50273     
50274     
50275     moveColumn : function(oldIndex, newIndex){
50276         var c = this.config[oldIndex];
50277         this.config.splice(oldIndex, 1);
50278         this.config.splice(newIndex, 0, c);
50279         this.dataMap = null;
50280         this.fireEvent("columnmoved", this, oldIndex, newIndex);
50281     },
50282
50283     isLocked : function(colIndex){
50284         return this.config[colIndex].locked === true;
50285     },
50286
50287     setLocked : function(colIndex, value, suppressEvent){
50288         if(this.isLocked(colIndex) == value){
50289             return;
50290         }
50291         this.config[colIndex].locked = value;
50292         if(!suppressEvent){
50293             this.fireEvent("columnlockchange", this, colIndex, value);
50294         }
50295     },
50296
50297     getTotalLockedWidth : function(){
50298         var totalWidth = 0;
50299         for(var i = 0; i < this.config.length; i++){
50300             if(this.isLocked(i) && !this.isHidden(i)){
50301                 this.totalWidth += this.getColumnWidth(i);
50302             }
50303         }
50304         return totalWidth;
50305     },
50306
50307     getLockedCount : function(){
50308         for(var i = 0, len = this.config.length; i < len; i++){
50309             if(!this.isLocked(i)){
50310                 return i;
50311             }
50312         }
50313     },
50314
50315     /**
50316      * Returns the number of columns.
50317      * @return {Number}
50318      */
50319     getColumnCount : function(visibleOnly){
50320         if(visibleOnly === true){
50321             var c = 0;
50322             for(var i = 0, len = this.config.length; i < len; i++){
50323                 if(!this.isHidden(i)){
50324                     c++;
50325                 }
50326             }
50327             return c;
50328         }
50329         return this.config.length;
50330     },
50331
50332     /**
50333      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
50334      * @param {Function} fn
50335      * @param {Object} scope (optional)
50336      * @return {Array} result
50337      */
50338     getColumnsBy : function(fn, scope){
50339         var r = [];
50340         for(var i = 0, len = this.config.length; i < len; i++){
50341             var c = this.config[i];
50342             if(fn.call(scope||this, c, i) === true){
50343                 r[r.length] = c;
50344             }
50345         }
50346         return r;
50347     },
50348
50349     /**
50350      * Returns true if the specified column is sortable.
50351      * @param {Number} col The column index
50352      * @return {Boolean}
50353      */
50354     isSortable : function(col){
50355         if(typeof this.config[col].sortable == "undefined"){
50356             return this.defaultSortable;
50357         }
50358         return this.config[col].sortable;
50359     },
50360
50361     /**
50362      * Returns the rendering (formatting) function defined for the column.
50363      * @param {Number} col The column index.
50364      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
50365      */
50366     getRenderer : function(col){
50367         if(!this.config[col].renderer){
50368             return Roo.grid.ColumnModel.defaultRenderer;
50369         }
50370         return this.config[col].renderer;
50371     },
50372
50373     /**
50374      * Sets the rendering (formatting) function for a column.
50375      * @param {Number} col The column index
50376      * @param {Function} fn The function to use to process the cell's raw data
50377      * to return HTML markup for the grid view. The render function is called with
50378      * the following parameters:<ul>
50379      * <li>Data value.</li>
50380      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
50381      * <li>css A CSS style string to apply to the table cell.</li>
50382      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
50383      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
50384      * <li>Row index</li>
50385      * <li>Column index</li>
50386      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
50387      */
50388     setRenderer : function(col, fn){
50389         this.config[col].renderer = fn;
50390     },
50391
50392     /**
50393      * Returns the width for the specified column.
50394      * @param {Number} col The column index
50395      * @return {Number}
50396      */
50397     getColumnWidth : function(col){
50398         return this.config[col].width * 1 || this.defaultWidth;
50399     },
50400
50401     /**
50402      * Sets the width for a column.
50403      * @param {Number} col The column index
50404      * @param {Number} width The new width
50405      */
50406     setColumnWidth : function(col, width, suppressEvent){
50407         this.config[col].width = width;
50408         this.totalWidth = null;
50409         if(!suppressEvent){
50410              this.fireEvent("widthchange", this, col, width);
50411         }
50412     },
50413
50414     /**
50415      * Returns the total width of all columns.
50416      * @param {Boolean} includeHidden True to include hidden column widths
50417      * @return {Number}
50418      */
50419     getTotalWidth : function(includeHidden){
50420         if(!this.totalWidth){
50421             this.totalWidth = 0;
50422             for(var i = 0, len = this.config.length; i < len; i++){
50423                 if(includeHidden || !this.isHidden(i)){
50424                     this.totalWidth += this.getColumnWidth(i);
50425                 }
50426             }
50427         }
50428         return this.totalWidth;
50429     },
50430
50431     /**
50432      * Returns the header for the specified column.
50433      * @param {Number} col The column index
50434      * @return {String}
50435      */
50436     getColumnHeader : function(col){
50437         return this.config[col].header;
50438     },
50439
50440     /**
50441      * Sets the header for a column.
50442      * @param {Number} col The column index
50443      * @param {String} header The new header
50444      */
50445     setColumnHeader : function(col, header){
50446         this.config[col].header = header;
50447         this.fireEvent("headerchange", this, col, header);
50448     },
50449
50450     /**
50451      * Returns the tooltip for the specified column.
50452      * @param {Number} col The column index
50453      * @return {String}
50454      */
50455     getColumnTooltip : function(col){
50456             return this.config[col].tooltip;
50457     },
50458     /**
50459      * Sets the tooltip for a column.
50460      * @param {Number} col The column index
50461      * @param {String} tooltip The new tooltip
50462      */
50463     setColumnTooltip : function(col, tooltip){
50464             this.config[col].tooltip = tooltip;
50465     },
50466
50467     /**
50468      * Returns the dataIndex for the specified column.
50469      * @param {Number} col The column index
50470      * @return {Number}
50471      */
50472     getDataIndex : function(col){
50473         return this.config[col].dataIndex;
50474     },
50475
50476     /**
50477      * Sets the dataIndex for a column.
50478      * @param {Number} col The column index
50479      * @param {Number} dataIndex The new dataIndex
50480      */
50481     setDataIndex : function(col, dataIndex){
50482         this.config[col].dataIndex = dataIndex;
50483     },
50484
50485     
50486     
50487     /**
50488      * Returns true if the cell is editable.
50489      * @param {Number} colIndex The column index
50490      * @param {Number} rowIndex The row index
50491      * @return {Boolean}
50492      */
50493     isCellEditable : function(colIndex, rowIndex){
50494         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
50495     },
50496
50497     /**
50498      * Returns the editor defined for the cell/column.
50499      * return false or null to disable editing.
50500      * @param {Number} colIndex The column index
50501      * @param {Number} rowIndex The row index
50502      * @return {Object}
50503      */
50504     getCellEditor : function(colIndex, rowIndex){
50505         return this.config[colIndex].editor;
50506     },
50507
50508     /**
50509      * Sets if a column is editable.
50510      * @param {Number} col The column index
50511      * @param {Boolean} editable True if the column is editable
50512      */
50513     setEditable : function(col, editable){
50514         this.config[col].editable = editable;
50515     },
50516
50517
50518     /**
50519      * Returns true if the column is hidden.
50520      * @param {Number} colIndex The column index
50521      * @return {Boolean}
50522      */
50523     isHidden : function(colIndex){
50524         return this.config[colIndex].hidden;
50525     },
50526
50527
50528     /**
50529      * Returns true if the column width cannot be changed
50530      */
50531     isFixed : function(colIndex){
50532         return this.config[colIndex].fixed;
50533     },
50534
50535     /**
50536      * Returns true if the column can be resized
50537      * @return {Boolean}
50538      */
50539     isResizable : function(colIndex){
50540         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
50541     },
50542     /**
50543      * Sets if a column is hidden.
50544      * @param {Number} colIndex The column index
50545      * @param {Boolean} hidden True if the column is hidden
50546      */
50547     setHidden : function(colIndex, hidden){
50548         this.config[colIndex].hidden = hidden;
50549         this.totalWidth = null;
50550         this.fireEvent("hiddenchange", this, colIndex, hidden);
50551     },
50552
50553     /**
50554      * Sets the editor for a column.
50555      * @param {Number} col The column index
50556      * @param {Object} editor The editor object
50557      */
50558     setEditor : function(col, editor){
50559         this.config[col].editor = editor;
50560     }
50561 });
50562
50563 Roo.grid.ColumnModel.defaultRenderer = function(value){
50564         if(typeof value == "string" && value.length < 1){
50565             return "&#160;";
50566         }
50567         return value;
50568 };
50569
50570 // Alias for backwards compatibility
50571 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
50572 /*
50573  * Based on:
50574  * Ext JS Library 1.1.1
50575  * Copyright(c) 2006-2007, Ext JS, LLC.
50576  *
50577  * Originally Released Under LGPL - original licence link has changed is not relivant.
50578  *
50579  * Fork - LGPL
50580  * <script type="text/javascript">
50581  */
50582
50583 /**
50584  * @class Roo.grid.AbstractSelectionModel
50585  * @extends Roo.util.Observable
50586  * Abstract base class for grid SelectionModels.  It provides the interface that should be
50587  * implemented by descendant classes.  This class should not be directly instantiated.
50588  * @constructor
50589  */
50590 Roo.grid.AbstractSelectionModel = function(){
50591     this.locked = false;
50592     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
50593 };
50594
50595 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
50596     /** @ignore Called by the grid automatically. Do not call directly. */
50597     init : function(grid){
50598         this.grid = grid;
50599         this.initEvents();
50600     },
50601
50602     /**
50603      * Locks the selections.
50604      */
50605     lock : function(){
50606         this.locked = true;
50607     },
50608
50609     /**
50610      * Unlocks the selections.
50611      */
50612     unlock : function(){
50613         this.locked = false;
50614     },
50615
50616     /**
50617      * Returns true if the selections are locked.
50618      * @return {Boolean}
50619      */
50620     isLocked : function(){
50621         return this.locked;
50622     }
50623 });/*
50624  * Based on:
50625  * Ext JS Library 1.1.1
50626  * Copyright(c) 2006-2007, Ext JS, LLC.
50627  *
50628  * Originally Released Under LGPL - original licence link has changed is not relivant.
50629  *
50630  * Fork - LGPL
50631  * <script type="text/javascript">
50632  */
50633 /**
50634  * @extends Roo.grid.AbstractSelectionModel
50635  * @class Roo.grid.RowSelectionModel
50636  * The default SelectionModel used by {@link Roo.grid.Grid}.
50637  * It supports multiple selections and keyboard selection/navigation. 
50638  * @constructor
50639  * @param {Object} config
50640  */
50641 Roo.grid.RowSelectionModel = function(config){
50642     Roo.apply(this, config);
50643     this.selections = new Roo.util.MixedCollection(false, function(o){
50644         return o.id;
50645     });
50646
50647     this.last = false;
50648     this.lastActive = false;
50649
50650     this.addEvents({
50651         /**
50652              * @event selectionchange
50653              * Fires when the selection changes
50654              * @param {SelectionModel} this
50655              */
50656             "selectionchange" : true,
50657         /**
50658              * @event afterselectionchange
50659              * Fires after the selection changes (eg. by key press or clicking)
50660              * @param {SelectionModel} this
50661              */
50662             "afterselectionchange" : true,
50663         /**
50664              * @event beforerowselect
50665              * Fires when a row is selected being selected, return false to cancel.
50666              * @param {SelectionModel} this
50667              * @param {Number} rowIndex The selected index
50668              * @param {Boolean} keepExisting False if other selections will be cleared
50669              */
50670             "beforerowselect" : true,
50671         /**
50672              * @event rowselect
50673              * Fires when a row is selected.
50674              * @param {SelectionModel} this
50675              * @param {Number} rowIndex The selected index
50676              * @param {Roo.data.Record} r The record
50677              */
50678             "rowselect" : true,
50679         /**
50680              * @event rowdeselect
50681              * Fires when a row is deselected.
50682              * @param {SelectionModel} this
50683              * @param {Number} rowIndex The selected index
50684              */
50685         "rowdeselect" : true
50686     });
50687     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
50688     this.locked = false;
50689 };
50690
50691 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
50692     /**
50693      * @cfg {Boolean} singleSelect
50694      * True to allow selection of only one row at a time (defaults to false)
50695      */
50696     singleSelect : false,
50697
50698     // private
50699     initEvents : function(){
50700
50701         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
50702             this.grid.on("mousedown", this.handleMouseDown, this);
50703         }else{ // allow click to work like normal
50704             this.grid.on("rowclick", this.handleDragableRowClick, this);
50705         }
50706
50707         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
50708             "up" : function(e){
50709                 if(!e.shiftKey){
50710                     this.selectPrevious(e.shiftKey);
50711                 }else if(this.last !== false && this.lastActive !== false){
50712                     var last = this.last;
50713                     this.selectRange(this.last,  this.lastActive-1);
50714                     this.grid.getView().focusRow(this.lastActive);
50715                     if(last !== false){
50716                         this.last = last;
50717                     }
50718                 }else{
50719                     this.selectFirstRow();
50720                 }
50721                 this.fireEvent("afterselectionchange", this);
50722             },
50723             "down" : function(e){
50724                 if(!e.shiftKey){
50725                     this.selectNext(e.shiftKey);
50726                 }else if(this.last !== false && this.lastActive !== false){
50727                     var last = this.last;
50728                     this.selectRange(this.last,  this.lastActive+1);
50729                     this.grid.getView().focusRow(this.lastActive);
50730                     if(last !== false){
50731                         this.last = last;
50732                     }
50733                 }else{
50734                     this.selectFirstRow();
50735                 }
50736                 this.fireEvent("afterselectionchange", this);
50737             },
50738             scope: this
50739         });
50740
50741         var view = this.grid.view;
50742         view.on("refresh", this.onRefresh, this);
50743         view.on("rowupdated", this.onRowUpdated, this);
50744         view.on("rowremoved", this.onRemove, this);
50745     },
50746
50747     // private
50748     onRefresh : function(){
50749         var ds = this.grid.dataSource, i, v = this.grid.view;
50750         var s = this.selections;
50751         s.each(function(r){
50752             if((i = ds.indexOfId(r.id)) != -1){
50753                 v.onRowSelect(i);
50754             }else{
50755                 s.remove(r);
50756             }
50757         });
50758     },
50759
50760     // private
50761     onRemove : function(v, index, r){
50762         this.selections.remove(r);
50763     },
50764
50765     // private
50766     onRowUpdated : function(v, index, r){
50767         if(this.isSelected(r)){
50768             v.onRowSelect(index);
50769         }
50770     },
50771
50772     /**
50773      * Select records.
50774      * @param {Array} records The records to select
50775      * @param {Boolean} keepExisting (optional) True to keep existing selections
50776      */
50777     selectRecords : function(records, keepExisting){
50778         if(!keepExisting){
50779             this.clearSelections();
50780         }
50781         var ds = this.grid.dataSource;
50782         for(var i = 0, len = records.length; i < len; i++){
50783             this.selectRow(ds.indexOf(records[i]), true);
50784         }
50785     },
50786
50787     /**
50788      * Gets the number of selected rows.
50789      * @return {Number}
50790      */
50791     getCount : function(){
50792         return this.selections.length;
50793     },
50794
50795     /**
50796      * Selects the first row in the grid.
50797      */
50798     selectFirstRow : function(){
50799         this.selectRow(0);
50800     },
50801
50802     /**
50803      * Select the last row.
50804      * @param {Boolean} keepExisting (optional) True to keep existing selections
50805      */
50806     selectLastRow : function(keepExisting){
50807         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
50808     },
50809
50810     /**
50811      * Selects the row immediately following the last selected row.
50812      * @param {Boolean} keepExisting (optional) True to keep existing selections
50813      */
50814     selectNext : function(keepExisting){
50815         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
50816             this.selectRow(this.last+1, keepExisting);
50817             this.grid.getView().focusRow(this.last);
50818         }
50819     },
50820
50821     /**
50822      * Selects the row that precedes the last selected row.
50823      * @param {Boolean} keepExisting (optional) True to keep existing selections
50824      */
50825     selectPrevious : function(keepExisting){
50826         if(this.last){
50827             this.selectRow(this.last-1, keepExisting);
50828             this.grid.getView().focusRow(this.last);
50829         }
50830     },
50831
50832     /**
50833      * Returns the selected records
50834      * @return {Array} Array of selected records
50835      */
50836     getSelections : function(){
50837         return [].concat(this.selections.items);
50838     },
50839
50840     /**
50841      * Returns the first selected record.
50842      * @return {Record}
50843      */
50844     getSelected : function(){
50845         return this.selections.itemAt(0);
50846     },
50847
50848
50849     /**
50850      * Clears all selections.
50851      */
50852     clearSelections : function(fast){
50853         if(this.locked) return;
50854         if(fast !== true){
50855             var ds = this.grid.dataSource;
50856             var s = this.selections;
50857             s.each(function(r){
50858                 this.deselectRow(ds.indexOfId(r.id));
50859             }, this);
50860             s.clear();
50861         }else{
50862             this.selections.clear();
50863         }
50864         this.last = false;
50865     },
50866
50867
50868     /**
50869      * Selects all rows.
50870      */
50871     selectAll : function(){
50872         if(this.locked) return;
50873         this.selections.clear();
50874         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
50875             this.selectRow(i, true);
50876         }
50877     },
50878
50879     /**
50880      * Returns True if there is a selection.
50881      * @return {Boolean}
50882      */
50883     hasSelection : function(){
50884         return this.selections.length > 0;
50885     },
50886
50887     /**
50888      * Returns True if the specified row is selected.
50889      * @param {Number/Record} record The record or index of the record to check
50890      * @return {Boolean}
50891      */
50892     isSelected : function(index){
50893         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
50894         return (r && this.selections.key(r.id) ? true : false);
50895     },
50896
50897     /**
50898      * Returns True if the specified record id is selected.
50899      * @param {String} id The id of record to check
50900      * @return {Boolean}
50901      */
50902     isIdSelected : function(id){
50903         return (this.selections.key(id) ? true : false);
50904     },
50905
50906     // private
50907     handleMouseDown : function(e, t){
50908         var view = this.grid.getView(), rowIndex;
50909         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
50910             return;
50911         };
50912         if(e.shiftKey && this.last !== false){
50913             var last = this.last;
50914             this.selectRange(last, rowIndex, e.ctrlKey);
50915             this.last = last; // reset the last
50916             view.focusRow(rowIndex);
50917         }else{
50918             var isSelected = this.isSelected(rowIndex);
50919             if(e.button !== 0 && isSelected){
50920                 view.focusRow(rowIndex);
50921             }else if(e.ctrlKey && isSelected){
50922                 this.deselectRow(rowIndex);
50923             }else if(!isSelected){
50924                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
50925                 view.focusRow(rowIndex);
50926             }
50927         }
50928         this.fireEvent("afterselectionchange", this);
50929     },
50930     // private
50931     handleDragableRowClick :  function(grid, rowIndex, e) 
50932     {
50933         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
50934             this.selectRow(rowIndex, false);
50935             grid.view.focusRow(rowIndex);
50936              this.fireEvent("afterselectionchange", this);
50937         }
50938     },
50939     
50940     /**
50941      * Selects multiple rows.
50942      * @param {Array} rows Array of the indexes of the row to select
50943      * @param {Boolean} keepExisting (optional) True to keep existing selections
50944      */
50945     selectRows : function(rows, keepExisting){
50946         if(!keepExisting){
50947             this.clearSelections();
50948         }
50949         for(var i = 0, len = rows.length; i < len; i++){
50950             this.selectRow(rows[i], true);
50951         }
50952     },
50953
50954     /**
50955      * Selects a range of rows. All rows in between startRow and endRow are also selected.
50956      * @param {Number} startRow The index of the first row in the range
50957      * @param {Number} endRow The index of the last row in the range
50958      * @param {Boolean} keepExisting (optional) True to retain existing selections
50959      */
50960     selectRange : function(startRow, endRow, keepExisting){
50961         if(this.locked) return;
50962         if(!keepExisting){
50963             this.clearSelections();
50964         }
50965         if(startRow <= endRow){
50966             for(var i = startRow; i <= endRow; i++){
50967                 this.selectRow(i, true);
50968             }
50969         }else{
50970             for(var i = startRow; i >= endRow; i--){
50971                 this.selectRow(i, true);
50972             }
50973         }
50974     },
50975
50976     /**
50977      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
50978      * @param {Number} startRow The index of the first row in the range
50979      * @param {Number} endRow The index of the last row in the range
50980      */
50981     deselectRange : function(startRow, endRow, preventViewNotify){
50982         if(this.locked) return;
50983         for(var i = startRow; i <= endRow; i++){
50984             this.deselectRow(i, preventViewNotify);
50985         }
50986     },
50987
50988     /**
50989      * Selects a row.
50990      * @param {Number} row The index of the row to select
50991      * @param {Boolean} keepExisting (optional) True to keep existing selections
50992      */
50993     selectRow : function(index, keepExisting, preventViewNotify){
50994         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
50995         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
50996             if(!keepExisting || this.singleSelect){
50997                 this.clearSelections();
50998             }
50999             var r = this.grid.dataSource.getAt(index);
51000             this.selections.add(r);
51001             this.last = this.lastActive = index;
51002             if(!preventViewNotify){
51003                 this.grid.getView().onRowSelect(index);
51004             }
51005             this.fireEvent("rowselect", this, index, r);
51006             this.fireEvent("selectionchange", this);
51007         }
51008     },
51009
51010     /**
51011      * Deselects a row.
51012      * @param {Number} row The index of the row to deselect
51013      */
51014     deselectRow : function(index, preventViewNotify){
51015         if(this.locked) return;
51016         if(this.last == index){
51017             this.last = false;
51018         }
51019         if(this.lastActive == index){
51020             this.lastActive = false;
51021         }
51022         var r = this.grid.dataSource.getAt(index);
51023         this.selections.remove(r);
51024         if(!preventViewNotify){
51025             this.grid.getView().onRowDeselect(index);
51026         }
51027         this.fireEvent("rowdeselect", this, index);
51028         this.fireEvent("selectionchange", this);
51029     },
51030
51031     // private
51032     restoreLast : function(){
51033         if(this._last){
51034             this.last = this._last;
51035         }
51036     },
51037
51038     // private
51039     acceptsNav : function(row, col, cm){
51040         return !cm.isHidden(col) && cm.isCellEditable(col, row);
51041     },
51042
51043     // private
51044     onEditorKey : function(field, e){
51045         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
51046         if(k == e.TAB){
51047             e.stopEvent();
51048             ed.completeEdit();
51049             if(e.shiftKey){
51050                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
51051             }else{
51052                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
51053             }
51054         }else if(k == e.ENTER && !e.ctrlKey){
51055             e.stopEvent();
51056             ed.completeEdit();
51057             if(e.shiftKey){
51058                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
51059             }else{
51060                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
51061             }
51062         }else if(k == e.ESC){
51063             ed.cancelEdit();
51064         }
51065         if(newCell){
51066             g.startEditing(newCell[0], newCell[1]);
51067         }
51068     }
51069 });/*
51070  * Based on:
51071  * Ext JS Library 1.1.1
51072  * Copyright(c) 2006-2007, Ext JS, LLC.
51073  *
51074  * Originally Released Under LGPL - original licence link has changed is not relivant.
51075  *
51076  * Fork - LGPL
51077  * <script type="text/javascript">
51078  */
51079 /**
51080  * @class Roo.grid.CellSelectionModel
51081  * @extends Roo.grid.AbstractSelectionModel
51082  * This class provides the basic implementation for cell selection in a grid.
51083  * @constructor
51084  * @param {Object} config The object containing the configuration of this model.
51085  */
51086 Roo.grid.CellSelectionModel = function(config){
51087     Roo.apply(this, config);
51088
51089     this.selection = null;
51090
51091     this.addEvents({
51092         /**
51093              * @event beforerowselect
51094              * Fires before a cell is selected.
51095              * @param {SelectionModel} this
51096              * @param {Number} rowIndex The selected row index
51097              * @param {Number} colIndex The selected cell index
51098              */
51099             "beforecellselect" : true,
51100         /**
51101              * @event cellselect
51102              * Fires when a cell is selected.
51103              * @param {SelectionModel} this
51104              * @param {Number} rowIndex The selected row index
51105              * @param {Number} colIndex The selected cell index
51106              */
51107             "cellselect" : true,
51108         /**
51109              * @event selectionchange
51110              * Fires when the active selection changes.
51111              * @param {SelectionModel} this
51112              * @param {Object} selection null for no selection or an object (o) with two properties
51113                 <ul>
51114                 <li>o.record: the record object for the row the selection is in</li>
51115                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
51116                 </ul>
51117              */
51118             "selectionchange" : true
51119     });
51120     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
51121 };
51122
51123 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
51124
51125     /** @ignore */
51126     initEvents : function(){
51127         this.grid.on("mousedown", this.handleMouseDown, this);
51128         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
51129         var view = this.grid.view;
51130         view.on("refresh", this.onViewChange, this);
51131         view.on("rowupdated", this.onRowUpdated, this);
51132         view.on("beforerowremoved", this.clearSelections, this);
51133         view.on("beforerowsinserted", this.clearSelections, this);
51134         if(this.grid.isEditor){
51135             this.grid.on("beforeedit", this.beforeEdit,  this);
51136         }
51137     },
51138
51139         //private
51140     beforeEdit : function(e){
51141         this.select(e.row, e.column, false, true, e.record);
51142     },
51143
51144         //private
51145     onRowUpdated : function(v, index, r){
51146         if(this.selection && this.selection.record == r){
51147             v.onCellSelect(index, this.selection.cell[1]);
51148         }
51149     },
51150
51151         //private
51152     onViewChange : function(){
51153         this.clearSelections(true);
51154     },
51155
51156         /**
51157          * Returns the currently selected cell,.
51158          * @return {Array} The selected cell (row, column) or null if none selected.
51159          */
51160     getSelectedCell : function(){
51161         return this.selection ? this.selection.cell : null;
51162     },
51163
51164     /**
51165      * Clears all selections.
51166      * @param {Boolean} true to prevent the gridview from being notified about the change.
51167      */
51168     clearSelections : function(preventNotify){
51169         var s = this.selection;
51170         if(s){
51171             if(preventNotify !== true){
51172                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
51173             }
51174             this.selection = null;
51175             this.fireEvent("selectionchange", this, null);
51176         }
51177     },
51178
51179     /**
51180      * Returns true if there is a selection.
51181      * @return {Boolean}
51182      */
51183     hasSelection : function(){
51184         return this.selection ? true : false;
51185     },
51186
51187     /** @ignore */
51188     handleMouseDown : function(e, t){
51189         var v = this.grid.getView();
51190         if(this.isLocked()){
51191             return;
51192         };
51193         var row = v.findRowIndex(t);
51194         var cell = v.findCellIndex(t);
51195         if(row !== false && cell !== false){
51196             this.select(row, cell);
51197         }
51198     },
51199
51200     /**
51201      * Selects a cell.
51202      * @param {Number} rowIndex
51203      * @param {Number} collIndex
51204      */
51205     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
51206         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
51207             this.clearSelections();
51208             r = r || this.grid.dataSource.getAt(rowIndex);
51209             this.selection = {
51210                 record : r,
51211                 cell : [rowIndex, colIndex]
51212             };
51213             if(!preventViewNotify){
51214                 var v = this.grid.getView();
51215                 v.onCellSelect(rowIndex, colIndex);
51216                 if(preventFocus !== true){
51217                     v.focusCell(rowIndex, colIndex);
51218                 }
51219             }
51220             this.fireEvent("cellselect", this, rowIndex, colIndex);
51221             this.fireEvent("selectionchange", this, this.selection);
51222         }
51223     },
51224
51225         //private
51226     isSelectable : function(rowIndex, colIndex, cm){
51227         return !cm.isHidden(colIndex);
51228     },
51229
51230     /** @ignore */
51231     handleKeyDown : function(e){
51232         //Roo.log('Cell Sel Model handleKeyDown');
51233         if(!e.isNavKeyPress()){
51234             return;
51235         }
51236         var g = this.grid, s = this.selection;
51237         if(!s){
51238             e.stopEvent();
51239             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
51240             if(cell){
51241                 this.select(cell[0], cell[1]);
51242             }
51243             return;
51244         }
51245         var sm = this;
51246         var walk = function(row, col, step){
51247             return g.walkCells(row, col, step, sm.isSelectable,  sm);
51248         };
51249         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
51250         var newCell;
51251
51252         switch(k){
51253             case e.TAB:
51254                 // handled by onEditorKey
51255                 if (g.isEditor && g.editing) {
51256                     return;
51257                 }
51258                 if(e.shiftKey){
51259                      newCell = walk(r, c-1, -1);
51260                 }else{
51261                      newCell = walk(r, c+1, 1);
51262                 }
51263              break;
51264              case e.DOWN:
51265                  newCell = walk(r+1, c, 1);
51266              break;
51267              case e.UP:
51268                  newCell = walk(r-1, c, -1);
51269              break;
51270              case e.RIGHT:
51271                  newCell = walk(r, c+1, 1);
51272              break;
51273              case e.LEFT:
51274                  newCell = walk(r, c-1, -1);
51275              break;
51276              case e.ENTER:
51277                  if(g.isEditor && !g.editing){
51278                     g.startEditing(r, c);
51279                     e.stopEvent();
51280                     return;
51281                 }
51282              break;
51283         };
51284         if(newCell){
51285             this.select(newCell[0], newCell[1]);
51286             e.stopEvent();
51287         }
51288     },
51289
51290     acceptsNav : function(row, col, cm){
51291         return !cm.isHidden(col) && cm.isCellEditable(col, row);
51292     },
51293     /**
51294      * Selects a cell.
51295      * @param {Number} field (not used) - as it's normally used as a listener
51296      * @param {Number} e - event - fake it by using
51297      *
51298      * var e = Roo.EventObjectImpl.prototype;
51299      * e.keyCode = e.TAB
51300      *
51301      * 
51302      */
51303     onEditorKey : function(field, e){
51304         
51305         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
51306         ///Roo.log('onEditorKey' + k);
51307         if (!ed) {
51308             
51309             
51310             
51311         }
51312         if(k == e.TAB){
51313             if(e.shiftKey){
51314                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
51315             }else{
51316                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
51317             }
51318             
51319             e.stopEvent();
51320             
51321         }else if(k == e.ENTER &&  !e.ctrlKey){
51322             ed.completeEdit();
51323             e.stopEvent();
51324             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
51325         }else if(k == e.ESC){
51326             ed.cancelEdit();
51327         }
51328         
51329         
51330         if(newCell){
51331             //Roo.log('next cell after edit');
51332             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
51333         }
51334     }
51335 });/*
51336  * Based on:
51337  * Ext JS Library 1.1.1
51338  * Copyright(c) 2006-2007, Ext JS, LLC.
51339  *
51340  * Originally Released Under LGPL - original licence link has changed is not relivant.
51341  *
51342  * Fork - LGPL
51343  * <script type="text/javascript">
51344  */
51345  
51346 /**
51347  * @class Roo.grid.EditorGrid
51348  * @extends Roo.grid.Grid
51349  * Class for creating and editable grid.
51350  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
51351  * The container MUST have some type of size defined for the grid to fill. The container will be 
51352  * automatically set to position relative if it isn't already.
51353  * @param {Object} dataSource The data model to bind to
51354  * @param {Object} colModel The column model with info about this grid's columns
51355  */
51356 Roo.grid.EditorGrid = function(container, config){
51357     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
51358     this.getGridEl().addClass("xedit-grid");
51359
51360     if(!this.selModel){
51361         this.selModel = new Roo.grid.CellSelectionModel();
51362     }
51363
51364     this.activeEditor = null;
51365
51366         this.addEvents({
51367             /**
51368              * @event beforeedit
51369              * Fires before cell editing is triggered. The edit event object has the following properties <br />
51370              * <ul style="padding:5px;padding-left:16px;">
51371              * <li>grid - This grid</li>
51372              * <li>record - The record being edited</li>
51373              * <li>field - The field name being edited</li>
51374              * <li>value - The value for the field being edited.</li>
51375              * <li>row - The grid row index</li>
51376              * <li>column - The grid column index</li>
51377              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
51378              * </ul>
51379              * @param {Object} e An edit event (see above for description)
51380              */
51381             "beforeedit" : true,
51382             /**
51383              * @event afteredit
51384              * Fires after a cell is edited. <br />
51385              * <ul style="padding:5px;padding-left:16px;">
51386              * <li>grid - This grid</li>
51387              * <li>record - The record being edited</li>
51388              * <li>field - The field name being edited</li>
51389              * <li>value - The value being set</li>
51390              * <li>originalValue - The original value for the field, before the edit.</li>
51391              * <li>row - The grid row index</li>
51392              * <li>column - The grid column index</li>
51393              * </ul>
51394              * @param {Object} e An edit event (see above for description)
51395              */
51396             "afteredit" : true,
51397             /**
51398              * @event validateedit
51399              * Fires after a cell is edited, but before the value is set in the record. 
51400          * You can use this to modify the value being set in the field, Return false
51401              * to cancel the change. The edit event object has the following properties <br />
51402              * <ul style="padding:5px;padding-left:16px;">
51403          * <li>editor - This editor</li>
51404              * <li>grid - This grid</li>
51405              * <li>record - The record being edited</li>
51406              * <li>field - The field name being edited</li>
51407              * <li>value - The value being set</li>
51408              * <li>originalValue - The original value for the field, before the edit.</li>
51409              * <li>row - The grid row index</li>
51410              * <li>column - The grid column index</li>
51411              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
51412              * </ul>
51413              * @param {Object} e An edit event (see above for description)
51414              */
51415             "validateedit" : true
51416         });
51417     this.on("bodyscroll", this.stopEditing,  this);
51418     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
51419 };
51420
51421 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
51422     /**
51423      * @cfg {Number} clicksToEdit
51424      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
51425      */
51426     clicksToEdit: 2,
51427
51428     // private
51429     isEditor : true,
51430     // private
51431     trackMouseOver: false, // causes very odd FF errors
51432
51433     onCellDblClick : function(g, row, col){
51434         this.startEditing(row, col);
51435     },
51436
51437     onEditComplete : function(ed, value, startValue){
51438         this.editing = false;
51439         this.activeEditor = null;
51440         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
51441         var r = ed.record;
51442         var field = this.colModel.getDataIndex(ed.col);
51443         var e = {
51444             grid: this,
51445             record: r,
51446             field: field,
51447             originalValue: startValue,
51448             value: value,
51449             row: ed.row,
51450             column: ed.col,
51451             cancel:false,
51452             editor: ed
51453         };
51454         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
51455         cell.show();
51456           
51457         if(String(value) !== String(startValue)){
51458             
51459             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
51460                 r.set(field, e.value);
51461                 // if we are dealing with a combo box..
51462                 // then we also set the 'name' colum to be the displayField
51463                 if (ed.field.displayField && ed.field.name) {
51464                     r.set(ed.field.name, ed.field.el.dom.value);
51465                 }
51466                 
51467                 delete e.cancel; //?? why!!!
51468                 this.fireEvent("afteredit", e);
51469             }
51470         } else {
51471             this.fireEvent("afteredit", e); // always fire it!
51472         }
51473         this.view.focusCell(ed.row, ed.col);
51474     },
51475
51476     /**
51477      * Starts editing the specified for the specified row/column
51478      * @param {Number} rowIndex
51479      * @param {Number} colIndex
51480      */
51481     startEditing : function(row, col){
51482         this.stopEditing();
51483         if(this.colModel.isCellEditable(col, row)){
51484             this.view.ensureVisible(row, col, true);
51485           
51486             var r = this.dataSource.getAt(row);
51487             var field = this.colModel.getDataIndex(col);
51488             var cell = Roo.get(this.view.getCell(row,col));
51489             var e = {
51490                 grid: this,
51491                 record: r,
51492                 field: field,
51493                 value: r.data[field],
51494                 row: row,
51495                 column: col,
51496                 cancel:false 
51497             };
51498             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
51499                 this.editing = true;
51500                 var ed = this.colModel.getCellEditor(col, row);
51501                 
51502                 if (!ed) {
51503                     return;
51504                 }
51505                 if(!ed.rendered){
51506                     ed.render(ed.parentEl || document.body);
51507                 }
51508                 ed.field.reset();
51509                
51510                 cell.hide();
51511                 
51512                 (function(){ // complex but required for focus issues in safari, ie and opera
51513                     ed.row = row;
51514                     ed.col = col;
51515                     ed.record = r;
51516                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
51517                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
51518                     this.activeEditor = ed;
51519                     var v = r.data[field];
51520                     ed.startEdit(this.view.getCell(row, col), v);
51521                     // combo's with 'displayField and name set
51522                     if (ed.field.displayField && ed.field.name) {
51523                         ed.field.el.dom.value = r.data[ed.field.name];
51524                     }
51525                     
51526                     
51527                 }).defer(50, this);
51528             }
51529         }
51530     },
51531         
51532     /**
51533      * Stops any active editing
51534      */
51535     stopEditing : function(){
51536         if(this.activeEditor){
51537             this.activeEditor.completeEdit();
51538         }
51539         this.activeEditor = null;
51540     }
51541 });/*
51542  * Based on:
51543  * Ext JS Library 1.1.1
51544  * Copyright(c) 2006-2007, Ext JS, LLC.
51545  *
51546  * Originally Released Under LGPL - original licence link has changed is not relivant.
51547  *
51548  * Fork - LGPL
51549  * <script type="text/javascript">
51550  */
51551
51552 // private - not really -- you end up using it !
51553 // This is a support class used internally by the Grid components
51554
51555 /**
51556  * @class Roo.grid.GridEditor
51557  * @extends Roo.Editor
51558  * Class for creating and editable grid elements.
51559  * @param {Object} config any settings (must include field)
51560  */
51561 Roo.grid.GridEditor = function(field, config){
51562     if (!config && field.field) {
51563         config = field;
51564         field = Roo.factory(config.field, Roo.form);
51565     }
51566     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
51567     field.monitorTab = false;
51568 };
51569
51570 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
51571     
51572     /**
51573      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
51574      */
51575     
51576     alignment: "tl-tl",
51577     autoSize: "width",
51578     hideEl : false,
51579     cls: "x-small-editor x-grid-editor",
51580     shim:false,
51581     shadow:"frame"
51582 });/*
51583  * Based on:
51584  * Ext JS Library 1.1.1
51585  * Copyright(c) 2006-2007, Ext JS, LLC.
51586  *
51587  * Originally Released Under LGPL - original licence link has changed is not relivant.
51588  *
51589  * Fork - LGPL
51590  * <script type="text/javascript">
51591  */
51592   
51593
51594   
51595 Roo.grid.PropertyRecord = Roo.data.Record.create([
51596     {name:'name',type:'string'},  'value'
51597 ]);
51598
51599
51600 Roo.grid.PropertyStore = function(grid, source){
51601     this.grid = grid;
51602     this.store = new Roo.data.Store({
51603         recordType : Roo.grid.PropertyRecord
51604     });
51605     this.store.on('update', this.onUpdate,  this);
51606     if(source){
51607         this.setSource(source);
51608     }
51609     Roo.grid.PropertyStore.superclass.constructor.call(this);
51610 };
51611
51612
51613
51614 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
51615     setSource : function(o){
51616         this.source = o;
51617         this.store.removeAll();
51618         var data = [];
51619         for(var k in o){
51620             if(this.isEditableValue(o[k])){
51621                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
51622             }
51623         }
51624         this.store.loadRecords({records: data}, {}, true);
51625     },
51626
51627     onUpdate : function(ds, record, type){
51628         if(type == Roo.data.Record.EDIT){
51629             var v = record.data['value'];
51630             var oldValue = record.modified['value'];
51631             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
51632                 this.source[record.id] = v;
51633                 record.commit();
51634                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
51635             }else{
51636                 record.reject();
51637             }
51638         }
51639     },
51640
51641     getProperty : function(row){
51642        return this.store.getAt(row);
51643     },
51644
51645     isEditableValue: function(val){
51646         if(val && val instanceof Date){
51647             return true;
51648         }else if(typeof val == 'object' || typeof val == 'function'){
51649             return false;
51650         }
51651         return true;
51652     },
51653
51654     setValue : function(prop, value){
51655         this.source[prop] = value;
51656         this.store.getById(prop).set('value', value);
51657     },
51658
51659     getSource : function(){
51660         return this.source;
51661     }
51662 });
51663
51664 Roo.grid.PropertyColumnModel = function(grid, store){
51665     this.grid = grid;
51666     var g = Roo.grid;
51667     g.PropertyColumnModel.superclass.constructor.call(this, [
51668         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
51669         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
51670     ]);
51671     this.store = store;
51672     this.bselect = Roo.DomHelper.append(document.body, {
51673         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
51674             {tag: 'option', value: 'true', html: 'true'},
51675             {tag: 'option', value: 'false', html: 'false'}
51676         ]
51677     });
51678     Roo.id(this.bselect);
51679     var f = Roo.form;
51680     this.editors = {
51681         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
51682         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
51683         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
51684         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
51685         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
51686     };
51687     this.renderCellDelegate = this.renderCell.createDelegate(this);
51688     this.renderPropDelegate = this.renderProp.createDelegate(this);
51689 };
51690
51691 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
51692     
51693     
51694     nameText : 'Name',
51695     valueText : 'Value',
51696     
51697     dateFormat : 'm/j/Y',
51698     
51699     
51700     renderDate : function(dateVal){
51701         return dateVal.dateFormat(this.dateFormat);
51702     },
51703
51704     renderBool : function(bVal){
51705         return bVal ? 'true' : 'false';
51706     },
51707
51708     isCellEditable : function(colIndex, rowIndex){
51709         return colIndex == 1;
51710     },
51711
51712     getRenderer : function(col){
51713         return col == 1 ?
51714             this.renderCellDelegate : this.renderPropDelegate;
51715     },
51716
51717     renderProp : function(v){
51718         return this.getPropertyName(v);
51719     },
51720
51721     renderCell : function(val){
51722         var rv = val;
51723         if(val instanceof Date){
51724             rv = this.renderDate(val);
51725         }else if(typeof val == 'boolean'){
51726             rv = this.renderBool(val);
51727         }
51728         return Roo.util.Format.htmlEncode(rv);
51729     },
51730
51731     getPropertyName : function(name){
51732         var pn = this.grid.propertyNames;
51733         return pn && pn[name] ? pn[name] : name;
51734     },
51735
51736     getCellEditor : function(colIndex, rowIndex){
51737         var p = this.store.getProperty(rowIndex);
51738         var n = p.data['name'], val = p.data['value'];
51739         
51740         if(typeof(this.grid.customEditors[n]) == 'string'){
51741             return this.editors[this.grid.customEditors[n]];
51742         }
51743         if(typeof(this.grid.customEditors[n]) != 'undefined'){
51744             return this.grid.customEditors[n];
51745         }
51746         if(val instanceof Date){
51747             return this.editors['date'];
51748         }else if(typeof val == 'number'){
51749             return this.editors['number'];
51750         }else if(typeof val == 'boolean'){
51751             return this.editors['boolean'];
51752         }else{
51753             return this.editors['string'];
51754         }
51755     }
51756 });
51757
51758 /**
51759  * @class Roo.grid.PropertyGrid
51760  * @extends Roo.grid.EditorGrid
51761  * This class represents the  interface of a component based property grid control.
51762  * <br><br>Usage:<pre><code>
51763  var grid = new Roo.grid.PropertyGrid("my-container-id", {
51764       
51765  });
51766  // set any options
51767  grid.render();
51768  * </code></pre>
51769   
51770  * @constructor
51771  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
51772  * The container MUST have some type of size defined for the grid to fill. The container will be
51773  * automatically set to position relative if it isn't already.
51774  * @param {Object} config A config object that sets properties on this grid.
51775  */
51776 Roo.grid.PropertyGrid = function(container, config){
51777     config = config || {};
51778     var store = new Roo.grid.PropertyStore(this);
51779     this.store = store;
51780     var cm = new Roo.grid.PropertyColumnModel(this, store);
51781     store.store.sort('name', 'ASC');
51782     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
51783         ds: store.store,
51784         cm: cm,
51785         enableColLock:false,
51786         enableColumnMove:false,
51787         stripeRows:false,
51788         trackMouseOver: false,
51789         clicksToEdit:1
51790     }, config));
51791     this.getGridEl().addClass('x-props-grid');
51792     this.lastEditRow = null;
51793     this.on('columnresize', this.onColumnResize, this);
51794     this.addEvents({
51795          /**
51796              * @event beforepropertychange
51797              * Fires before a property changes (return false to stop?)
51798              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
51799              * @param {String} id Record Id
51800              * @param {String} newval New Value
51801          * @param {String} oldval Old Value
51802              */
51803         "beforepropertychange": true,
51804         /**
51805              * @event propertychange
51806              * Fires after a property changes
51807              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
51808              * @param {String} id Record Id
51809              * @param {String} newval New Value
51810          * @param {String} oldval Old Value
51811              */
51812         "propertychange": true
51813     });
51814     this.customEditors = this.customEditors || {};
51815 };
51816 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
51817     
51818      /**
51819      * @cfg {Object} customEditors map of colnames=> custom editors.
51820      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
51821      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
51822      * false disables editing of the field.
51823          */
51824     
51825       /**
51826      * @cfg {Object} propertyNames map of property Names to their displayed value
51827          */
51828     
51829     render : function(){
51830         Roo.grid.PropertyGrid.superclass.render.call(this);
51831         this.autoSize.defer(100, this);
51832     },
51833
51834     autoSize : function(){
51835         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
51836         if(this.view){
51837             this.view.fitColumns();
51838         }
51839     },
51840
51841     onColumnResize : function(){
51842         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
51843         this.autoSize();
51844     },
51845     /**
51846      * Sets the data for the Grid
51847      * accepts a Key => Value object of all the elements avaiable.
51848      * @param {Object} data  to appear in grid.
51849      */
51850     setSource : function(source){
51851         this.store.setSource(source);
51852         //this.autoSize();
51853     },
51854     /**
51855      * Gets all the data from the grid.
51856      * @return {Object} data  data stored in grid
51857      */
51858     getSource : function(){
51859         return this.store.getSource();
51860     }
51861 });/*
51862  * Based on:
51863  * Ext JS Library 1.1.1
51864  * Copyright(c) 2006-2007, Ext JS, LLC.
51865  *
51866  * Originally Released Under LGPL - original licence link has changed is not relivant.
51867  *
51868  * Fork - LGPL
51869  * <script type="text/javascript">
51870  */
51871  
51872 /**
51873  * @class Roo.LoadMask
51874  * A simple utility class for generically masking elements while loading data.  If the element being masked has
51875  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
51876  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
51877  * element's UpdateManager load indicator and will be destroyed after the initial load.
51878  * @constructor
51879  * Create a new LoadMask
51880  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
51881  * @param {Object} config The config object
51882  */
51883 Roo.LoadMask = function(el, config){
51884     this.el = Roo.get(el);
51885     Roo.apply(this, config);
51886     if(this.store){
51887         this.store.on('beforeload', this.onBeforeLoad, this);
51888         this.store.on('load', this.onLoad, this);
51889         this.store.on('loadexception', this.onLoad, this);
51890         this.removeMask = false;
51891     }else{
51892         var um = this.el.getUpdateManager();
51893         um.showLoadIndicator = false; // disable the default indicator
51894         um.on('beforeupdate', this.onBeforeLoad, this);
51895         um.on('update', this.onLoad, this);
51896         um.on('failure', this.onLoad, this);
51897         this.removeMask = true;
51898     }
51899 };
51900
51901 Roo.LoadMask.prototype = {
51902     /**
51903      * @cfg {Boolean} removeMask
51904      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
51905      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
51906      */
51907     /**
51908      * @cfg {String} msg
51909      * The text to display in a centered loading message box (defaults to 'Loading...')
51910      */
51911     msg : 'Loading...',
51912     /**
51913      * @cfg {String} msgCls
51914      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
51915      */
51916     msgCls : 'x-mask-loading',
51917
51918     /**
51919      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
51920      * @type Boolean
51921      */
51922     disabled: false,
51923
51924     /**
51925      * Disables the mask to prevent it from being displayed
51926      */
51927     disable : function(){
51928        this.disabled = true;
51929     },
51930
51931     /**
51932      * Enables the mask so that it can be displayed
51933      */
51934     enable : function(){
51935         this.disabled = false;
51936     },
51937
51938     // private
51939     onLoad : function(){
51940         this.el.unmask(this.removeMask);
51941     },
51942
51943     // private
51944     onBeforeLoad : function(){
51945         if(!this.disabled){
51946             this.el.mask(this.msg, this.msgCls);
51947         }
51948     },
51949
51950     // private
51951     destroy : function(){
51952         if(this.store){
51953             this.store.un('beforeload', this.onBeforeLoad, this);
51954             this.store.un('load', this.onLoad, this);
51955             this.store.un('loadexception', this.onLoad, this);
51956         }else{
51957             var um = this.el.getUpdateManager();
51958             um.un('beforeupdate', this.onBeforeLoad, this);
51959             um.un('update', this.onLoad, this);
51960             um.un('failure', this.onLoad, this);
51961         }
51962     }
51963 };/*
51964  * Based on:
51965  * Ext JS Library 1.1.1
51966  * Copyright(c) 2006-2007, Ext JS, LLC.
51967  *
51968  * Originally Released Under LGPL - original licence link has changed is not relivant.
51969  *
51970  * Fork - LGPL
51971  * <script type="text/javascript">
51972  */
51973 Roo.XTemplate = function(){
51974     Roo.XTemplate.superclass.constructor.apply(this, arguments);
51975     var s = this.html;
51976
51977     s = ['<tpl>', s, '</tpl>'].join('');
51978
51979     var re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/;
51980
51981     var nameRe = /^<tpl\b[^>]*?for="(.*?)"/;
51982     var ifRe = /^<tpl\b[^>]*?if="(.*?)"/;
51983     var execRe = /^<tpl\b[^>]*?exec="(.*?)"/;
51984     var m, id = 0;
51985     var tpls = [];
51986
51987     while(m = s.match(re)){
51988        var m2 = m[0].match(nameRe);
51989        var m3 = m[0].match(ifRe);
51990        var m4 = m[0].match(execRe);
51991        var exp = null, fn = null, exec = null;
51992        var name = m2 && m2[1] ? m2[1] : '';
51993        if(m3){
51994            exp = m3 && m3[1] ? m3[1] : null;
51995            if(exp){
51996                fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
51997            }
51998        }
51999        if(m4){
52000            exp = m4 && m4[1] ? m4[1] : null;
52001            if(exp){
52002                exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
52003            }
52004        }
52005        if(name){
52006            switch(name){
52007                case '.': name = new Function('values', 'parent', 'with(values){ return values; }'); break;
52008                case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
52009                default: name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
52010            }
52011        }
52012        tpls.push({
52013             id: id,
52014             target: name,
52015             exec: exec,
52016             test: fn,
52017             body: m[1]||''
52018         });
52019        s = s.replace(m[0], '{xtpl'+ id + '}');
52020        ++id;
52021     }
52022     for(var i = tpls.length-1; i >= 0; --i){
52023         this.compileTpl(tpls[i]);
52024     }
52025     this.master = tpls[tpls.length-1];
52026     this.tpls = tpls;
52027 };
52028 Roo.extend(Roo.XTemplate, Roo.Template, {
52029
52030     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
52031
52032     applySubTemplate : function(id, values, parent){
52033         var t = this.tpls[id];
52034         if(t.test && !t.test.call(this, values, parent)){
52035             return '';
52036         }
52037         if(t.exec && t.exec.call(this, values, parent)){
52038             return '';
52039         }
52040         var vs = t.target ? t.target.call(this, values, parent) : values;
52041         parent = t.target ? values : parent;
52042         if(t.target && vs instanceof Array){
52043             var buf = [];
52044             for(var i = 0, len = vs.length; i < len; i++){
52045                 buf[buf.length] = t.compiled.call(this, vs[i], parent);
52046             }
52047             return buf.join('');
52048         }
52049         return t.compiled.call(this, vs, parent);
52050     },
52051
52052     compileTpl : function(tpl){
52053         var fm = Roo.util.Format;
52054         var useF = this.disableFormats !== true;
52055         var sep = Roo.isGecko ? "+" : ",";
52056         var fn = function(m, name, format, args){
52057             if(name.substr(0, 4) == 'xtpl'){
52058                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
52059             }
52060             var v;
52061             if(name.indexOf('.') != -1){
52062                 v = name;
52063             }else{
52064                 v = "values['" + name + "']";
52065             }
52066             if(format && useF){
52067                 args = args ? ',' + args : "";
52068                 if(format.substr(0, 5) != "this."){
52069                     format = "fm." + format + '(';
52070                 }else{
52071                     format = 'this.call("'+ format.substr(5) + '", ';
52072                     args = ", values";
52073                 }
52074             }else{
52075                 args= ''; format = "("+v+" === undefined ? '' : ";
52076             }
52077             return "'"+ sep + format + v + args + ")"+sep+"'";
52078         };
52079         var body;
52080         // branched to use + in gecko and [].join() in others
52081         if(Roo.isGecko){
52082             body = "tpl.compiled = function(values, parent){ return '" +
52083                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
52084                     "';};";
52085         }else{
52086             body = ["tpl.compiled = function(values, parent){ return ['"];
52087             body.push(tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
52088             body.push("'].join('');};");
52089             body = body.join('');
52090         }
52091         /** eval:var:zzzzzzz */
52092         eval(body);
52093         return this;
52094     },
52095
52096     applyTemplate : function(values){
52097         return this.master.compiled.call(this, values, {});
52098         var s = this.subs;
52099     },
52100
52101     apply : function(){
52102         return this.applyTemplate.apply(this, arguments);
52103     },
52104
52105     compile : function(){return this;}
52106 });
52107
52108 Roo.XTemplate.from = function(el){
52109     el = Roo.getDom(el);
52110     return new Roo.XTemplate(el.value || el.innerHTML);
52111 };/*
52112  * Original code for Roojs - LGPL
52113  * <script type="text/javascript">
52114  */
52115  
52116 /**
52117  * @class Roo.XComponent
52118  * A delayed Element creator...
52119  * Or a way to group chunks of interface together.
52120  * 
52121  * Mypart.xyx = new Roo.XComponent({
52122
52123     parent : 'Mypart.xyz', // empty == document.element.!!
52124     order : '001',
52125     name : 'xxxx'
52126     region : 'xxxx'
52127     disabled : function() {} 
52128      
52129     tree : function() { // return an tree of xtype declared components
52130         var MODULE = this;
52131         return 
52132         {
52133             xtype : 'NestedLayoutPanel',
52134             // technicall
52135         }
52136      ]
52137  *})
52138  *
52139  *
52140  * It can be used to build a big heiracy, with parent etc.
52141  * or you can just use this to render a single compoent to a dom element
52142  * MYPART.render(Roo.Element | String(id) | dom_element )
52143  * 
52144  * @extends Roo.util.Observable
52145  * @constructor
52146  * @param cfg {Object} configuration of component
52147  * 
52148  */
52149 Roo.XComponent = function(cfg) {
52150     Roo.apply(this, cfg);
52151     this.addEvents({ 
52152         /**
52153              * @event built
52154              * Fires when this the componnt is built
52155              * @param {Roo.XComponent} c the component
52156              */
52157         'built' : true,
52158         /**
52159              * @event buildcomplete
52160              * Fires on the top level element when all elements have been built
52161              * @param {Roo.XComponent} c the top level component.
52162          */
52163         'buildcomplete' : true
52164         
52165     });
52166     this.region = this.region || 'center'; // default..
52167     Roo.XComponent.register(this);
52168     this.modules = false;
52169     this.el = false; // where the layout goes..
52170     
52171     
52172 }
52173 Roo.extend(Roo.XComponent, Roo.util.Observable, {
52174     /**
52175      * @property el
52176      * The created element (with Roo.factory())
52177      * @type {Roo.Layout}
52178      */
52179     el  : false,
52180     
52181     /**
52182      * @property el
52183      * for BC  - use el in new code
52184      * @type {Roo.Layout}
52185      */
52186     panel : false,
52187     
52188     /**
52189      * @property layout
52190      * for BC  - use el in new code
52191      * @type {Roo.Layout}
52192      */
52193     layout : false,
52194     
52195      /**
52196      * @cfg {Function|boolean} disabled
52197      * If this module is disabled by some rule, return true from the funtion
52198      */
52199     disabled : false,
52200     
52201     /**
52202      * @cfg {String} parent 
52203      * Name of parent element which it get xtype added to..
52204      */
52205     parent: false,
52206     
52207     /**
52208      * @cfg {String} order
52209      * Used to set the order in which elements are created (usefull for multiple tabs)
52210      */
52211     
52212     order : false,
52213     /**
52214      * @cfg {String} name
52215      * String to display while loading.
52216      */
52217     name : false,
52218     /**
52219      * @cfg {String} region
52220      * Region to render component to (defaults to center)
52221      */
52222     region : 'center',
52223     
52224     /**
52225      * @cfg {Array} items
52226      * A single item array - the first element is the root of the tree..
52227      * It's done this way to stay compatible with the Xtype system...
52228      */
52229     items : false,
52230     
52231     
52232      /**
52233      * render
52234      * render element to dom or tree
52235      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
52236      */
52237     
52238     render : function(el)
52239     {
52240         
52241         el = el || false;
52242         var hp = this.parent ? 1 : 0;
52243         
52244         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
52245             // if parent is a '#.....' string, then let's use that..
52246             var ename = this.parent.substr(1)
52247             this.parent = false;
52248             el = Roo.get(ename);
52249             if (!el) {
52250                 Roo.log("Warning - element can not be found :#" + ename );
52251                 return;
52252             }
52253         }
52254         
52255         
52256         if (!this.parent) {
52257             
52258             el = el ? Roo.get(el) : false;
52259             
52260             // it's a top level one..
52261             this.parent =  {
52262                 el : new Roo.BorderLayout(el || document.body, {
52263                 
52264                      center: {
52265                          titlebar: false,
52266                          autoScroll:false,
52267                          closeOnTab: true,
52268                          tabPosition: 'top',
52269                           //resizeTabs: true,
52270                          alwaysShowTabs: el && hp? false :  true,
52271                          hideTabs: el || !hp ? true :  false,
52272                          minTabWidth: 140
52273                      }
52274                  })
52275             }
52276         }
52277         
52278         
52279             
52280         var tree = this.tree();
52281         tree.region = tree.region || this.region;
52282         this.el = this.parent.el.addxtype(tree);
52283         this.fireEvent('built', this);
52284         
52285         this.panel = this.el;
52286         this.layout = this.panel.layout;    
52287          
52288     }
52289     
52290 });
52291
52292 Roo.apply(Roo.XComponent, {
52293     
52294     /**
52295      * @property  buildCompleted
52296      * True when the builder has completed building the interface.
52297      * @type Boolean
52298      */
52299     buildCompleted : false,
52300      
52301     /**
52302      * @property  topModule
52303      * the upper most module - uses document.element as it's constructor.
52304      * @type Object
52305      */
52306      
52307     topModule  : false,
52308       
52309     /**
52310      * @property  modules
52311      * array of modules to be created by registration system.
52312      * @type {Array} of Roo.XComponent
52313      */
52314     
52315     modules : [],
52316     /**
52317      * @property  elmodules
52318      * array of modules to be created by which use #ID 
52319      * @type {Array} of Roo.XComponent
52320      */
52321      
52322     elmodules : [],
52323
52324     
52325     /**
52326      * Register components to be built later.
52327      *
52328      * This solves the following issues
52329      * - Building is not done on page load, but after an authentication process has occured.
52330      * - Interface elements are registered on page load
52331      * - Parent Interface elements may not be loaded before child, so this handles that..
52332      * 
52333      *
52334      * example:
52335      * 
52336      * MyApp.register({
52337           order : '000001',
52338           module : 'Pman.Tab.projectMgr',
52339           region : 'center',
52340           parent : 'Pman.layout',
52341           disabled : false,  // or use a function..
52342         })
52343      
52344      * * @param {Object} details about module
52345      */
52346     register : function(obj) {
52347         this.modules.push(obj);
52348          
52349     },
52350     /**
52351      * convert a string to an object..
52352      * eg. 'AAA.BBB' -> finds AAA.BBB
52353
52354      */
52355     
52356     toObject : function(str)
52357     {
52358         if (!str || typeof(str) == 'object') {
52359             return str;
52360         }
52361         if (str.substring(0,1) == '#') {
52362             return str;
52363         }
52364
52365         var ar = str.split('.');
52366         var rt, o;
52367         rt = ar.shift();
52368             /** eval:var:o */
52369         try {
52370             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
52371         } catch (e) {
52372             throw "Module not found : " + str;
52373         }
52374         
52375         if (o === false) {
52376             throw "Module not found : " + str;
52377         }
52378         Roo.each(ar, function(e) {
52379             if (typeof(o[e]) == 'undefined') {
52380                 throw "Module not found : " + str;
52381             }
52382             o = o[e];
52383         });
52384         
52385         return o;
52386         
52387     },
52388     
52389     
52390     /**
52391      * move modules into their correct place in the tree..
52392      * 
52393      */
52394     preBuild : function ()
52395     {
52396         var _t = this;
52397         Roo.each(this.modules , function (obj)
52398         {
52399             var opar = obj.parent;
52400             try { 
52401                 obj.parent = this.toObject(opar);
52402             } catch(e) {
52403                 Roo.log(e.toString());
52404                 return;
52405             }
52406             
52407             if (!obj.parent) {
52408                 this.topModule = obj;
52409                 return;
52410             }
52411             if (typeof(obj.parent) == 'string') {
52412                 this.elmodules.push(obj);
52413                 return;
52414             }
52415             if (obj.parent.constructor != Roo.XComponent) {
52416                 Roo.log("Object Parent is not instance of XComponent:" + obj.name)
52417             }
52418             if (!obj.parent.modules) {
52419                 obj.parent.modules = new Roo.util.MixedCollection(false, 
52420                     function(o) { return o.order + '' }
52421                 );
52422             }
52423             
52424             obj.parent.modules.add(obj);
52425         }, this);
52426     },
52427     
52428      /**
52429      * make a list of modules to build.
52430      * @return {Array} list of modules. 
52431      */ 
52432     
52433     buildOrder : function()
52434     {
52435         var _this = this;
52436         var cmp = function(a,b) {   
52437             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
52438         };
52439         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
52440             throw "No top level modules to build";
52441         }
52442         
52443         // make a flat list in order of modules to build.
52444         var mods = this.topModule ? [ this.topModule ] : [];
52445         Roo.each(this.elmodules,function(e) { mods.push(e) });
52446
52447         
52448         // add modules to their parents..
52449         var addMod = function(m) {
52450            // Roo.debug && Roo.log(m.modKey);
52451             
52452             mods.push(m);
52453             if (m.modules) {
52454                 m.modules.keySort('ASC',  cmp );
52455                 m.modules.each(addMod);
52456             }
52457             // not sure if this is used any more..
52458             if (m.finalize) {
52459                 m.finalize.name = m.name + " (clean up) ";
52460                 mods.push(m.finalize);
52461             }
52462             
52463         }
52464         if (this.topModule) { 
52465             this.topModule.modules.keySort('ASC',  cmp );
52466             this.topModule.modules.each(addMod);
52467         }
52468         return mods;
52469     },
52470     
52471      /**
52472      * Build the registered modules.
52473      * @param {Object} parent element.
52474      * @param {Function} optional method to call after module has been added.
52475      * 
52476      */ 
52477    
52478     build : function() 
52479     {
52480         
52481         this.preBuild();
52482         var mods = this.buildOrder();
52483       
52484         //this.allmods = mods;
52485         //Roo.debug && Roo.log(mods);
52486         //return;
52487         if (!mods.length) { // should not happen
52488             throw "NO modules!!!";
52489         }
52490         
52491         
52492         
52493         // flash it up as modal - so we store the mask!?
52494         Roo.MessageBox.show({ title: 'loading' });
52495         Roo.MessageBox.show({
52496            title: "Please wait...",
52497            msg: "Building Interface...",
52498            width:450,
52499            progress:true,
52500            closable:false,
52501            modal: false
52502           
52503         });
52504         var total = mods.length;
52505         
52506         var _this = this;
52507         var progressRun = function() {
52508             if (!mods.length) {
52509                 Roo.debug && Roo.log('hide?');
52510                 Roo.MessageBox.hide();
52511                 if (_this.topModule) { 
52512                     _this.topModule.fireEvent('buildcomplete', _this.topModule);
52513                 }
52514                 // THE END...
52515                 return false;   
52516             }
52517             
52518             var m = mods.shift();
52519             
52520             
52521             Roo.debug && Roo.log(m);
52522             // not sure if this is supported any more.. - modules that are are just function
52523             if (typeof(m) == 'function') { 
52524                 m.call(this);
52525                 return progressRun.defer(10, _this);
52526             } 
52527             
52528             
52529             
52530             Roo.MessageBox.updateProgress(
52531                 (total  - mods.length)/total,  "Building Interface " + (total  - mods.length) + 
52532                     " of " + total + 
52533                     (m.name ? (' - ' + m.name) : '')
52534                     );
52535             
52536          
52537             // is the module disabled?
52538             var disabled = (typeof(m.disabled) == 'function') ?
52539                 m.disabled.call(m.module.disabled) : m.disabled;    
52540             
52541             
52542             if (disabled) {
52543                 return progressRun(); // we do not update the display!
52544             }
52545             
52546             // now build 
52547             
52548             m.render();
52549             // it's 10 on top level, and 1 on others??? why...
52550             return progressRun.defer(10, _this);
52551              
52552         }
52553         progressRun.defer(1, _this);
52554      
52555         
52556         
52557     }
52558     
52559      
52560    
52561     
52562     
52563 });
52564  //<script type="text/javascript">
52565
52566
52567 /**
52568  * @class Roo.Login
52569  * @extends Roo.LayoutDialog
52570  * A generic Login Dialog..... - only one needed in theory!?!?
52571  *
52572  * Fires XComponent builder on success...
52573  * 
52574  * Sends 
52575  *    username,password, lang = for login actions.
52576  *    check = 1 for periodic checking that sesion is valid.
52577  *    passwordRequest = email request password
52578  *    logout = 1 = to logout
52579  * 
52580  * Affects: (this id="????" elements)
52581  *   loading  (removed) (used to indicate application is loading)
52582  *   loading-mask (hides) (used to hide application when it's building loading)
52583  *   
52584  * 
52585  * Usage: 
52586  *    
52587  * 
52588  * Myapp.login = Roo.Login({
52589      url: xxxx,
52590    
52591      realm : 'Myapp', 
52592      
52593      
52594      method : 'POST',
52595      
52596      
52597      * 
52598  })
52599  * 
52600  * 
52601  * 
52602  **/
52603  
52604 Roo.Login = function(cfg)
52605 {
52606     this.addEvents({
52607         'refreshed' : true
52608     });
52609     
52610     Roo.apply(this,cfg);
52611     
52612     Roo.onReady(function() {
52613         this.onLoad();
52614     }, this);
52615     // call parent..
52616     
52617    
52618     Roo.Login.superclass.constructor.call(this, this);
52619     //this.addxtype(this.items[0]);
52620     
52621     
52622 }
52623
52624
52625 Roo.extend(Roo.Login, Roo.LayoutDialog, {
52626     
52627     /**
52628      * @cfg {String} method
52629      * Method used to query for login details.
52630      */
52631     
52632     method : 'POST',
52633     /**
52634      * @cfg {String} url
52635      * URL to query login data. - eg. baseURL + '/Login.php'
52636      */
52637     url : '',
52638     
52639     /**
52640      * @property user
52641      * The user data - if user.id < 0 then login will be bypassed. (used for inital setup situation.
52642      * @type {Object} 
52643      */
52644     user : false,
52645     /**
52646      * @property checkFails
52647      * Number of times we have attempted to get authentication check, and failed.
52648      * @type {Number} 
52649      */
52650     checkFails : 0,
52651       /**
52652      * @property intervalID
52653      * The window interval that does the constant login checking.
52654      * @type {Number} 
52655      */
52656     intervalID : 0,
52657     
52658     
52659     onLoad : function() // called on page load...
52660     {
52661         // load 
52662          
52663         if (Roo.get('loading')) { // clear any loading indicator..
52664             Roo.get('loading').remove();
52665         }
52666         
52667         //this.switchLang('en'); // set the language to english..
52668        
52669         this.check({
52670             success:  function(response, opts)  {  // check successfull...
52671             
52672                 var res = this.processResponse(response);
52673                 this.checkFails =0;
52674                 if (!res.success) { // error!
52675                     this.checkFails = 5;
52676                     //console.log('call failure');
52677                     return this.failure(response,opts);
52678                 }
52679                 
52680                 if (!res.data.id) { // id=0 == login failure.
52681                     return this.show();
52682                 }
52683                 
52684                               
52685                         //console.log(success);
52686                 this.fillAuth(res.data);   
52687                 this.checkFails =0;
52688                 Roo.XComponent.build();
52689             },
52690             failure : this.show
52691         });
52692         
52693     }, 
52694     
52695     
52696     check: function(cfg) // called every so often to refresh cookie etc..
52697     {
52698         if (cfg.again) { // could be undefined..
52699             this.checkFails++;
52700         } else {
52701             this.checkFails = 0;
52702         }
52703         var _this = this;
52704         if (this.sending) {
52705             if ( this.checkFails > 4) {
52706                 Roo.MessageBox.alert("Error",  
52707                     "Error getting authentication status. - try reloading, or wait a while", function() {
52708                         _this.sending = false;
52709                     }); 
52710                 return;
52711             }
52712             cfg.again = true;
52713             _this.check.defer(10000, _this, [ cfg ]); // check in 10 secs.
52714             return;
52715         }
52716         this.sending = true;
52717         
52718         Roo.Ajax.request({  
52719             url: this.url,
52720             params: {
52721                 getAuthUser: true
52722             },  
52723             method: this.method,
52724             success:  cfg.success || this.success,
52725             failure : cfg.failure || this.failure,
52726             scope : this,
52727             callCfg : cfg
52728               
52729         });  
52730     }, 
52731     
52732     
52733     logout: function()
52734     {
52735         window.onbeforeunload = function() { }; // false does not work for IE..
52736         this.user = false;
52737         var _this = this;
52738         
52739         Roo.Ajax.request({  
52740             url: this.url,
52741             params: {
52742                 logout: 1
52743             },  
52744             method: 'GET',
52745             failure : function() {
52746                 Roo.MessageBox.alert("Error", "Error logging out. - continuing anyway.", function() {
52747                     document.location = document.location.toString() + '?ts=' + Math.random();
52748                 });
52749                 
52750             },
52751             success : function() {
52752                 _this.user = false;
52753                 this.checkFails =0;
52754                 // fixme..
52755                 document.location = document.location.toString() + '?ts=' + Math.random();
52756             }
52757               
52758               
52759         }); 
52760     },
52761     
52762     processResponse : function (response)
52763     {
52764         var res = '';
52765         try {
52766             res = Roo.decode(response.responseText);
52767             // oops...
52768             if (typeof(res) != 'object') {
52769                 res = { success : false, errorMsg : res, errors : true };
52770             }
52771             if (typeof(res.success) == 'undefined') {
52772                 res.success = false;
52773             }
52774             
52775         } catch(e) {
52776             res = { success : false,  errorMsg : response.responseText, errors : true };
52777         }
52778         return res;
52779     },
52780     
52781     success : function(response, opts)  // check successfull...
52782     {  
52783         this.sending = false;
52784         var res = this.processResponse(response);
52785         if (!res.success) {
52786             return this.failure(response, opts);
52787         }
52788         if (!res.data || !res.data.id) {
52789             return this.failure(response,opts);
52790         }
52791         //console.log(res);
52792         this.fillAuth(res.data);
52793         
52794         this.checkFails =0;
52795         
52796     },
52797     
52798     
52799     failure : function (response, opts) // called if login 'check' fails.. (causes re-check)
52800     {
52801         this.authUser = -1;
52802         this.sending = false;
52803         var res = this.processResponse(response);
52804         //console.log(res);
52805         if ( this.checkFails > 2) {
52806         
52807             Roo.MessageBox.alert("Error", res.errorMsg ? res.errorMsg : 
52808                 "Error getting authentication status. - try reloading"); 
52809             return;
52810         }
52811         opts.callCfg.again = true;
52812         this.check.defer(1000, this, [ opts.callCfg ]);
52813         return;  
52814     },
52815     
52816     
52817     
52818     fillAuth: function(au) {
52819         this.startAuthCheck();
52820         this.authUserId = au.id;
52821         this.authUser = au;
52822         this.lastChecked = new Date();
52823         this.fireEvent('refreshed', au);
52824         //Pman.Tab.FaxQueue.newMaxId(au.faxMax);
52825         //Pman.Tab.FaxTab.setTitle(au.faxNumPending);
52826         au.lang = au.lang || 'en';
52827         //this.switchLang(Roo.state.Manager.get('Pman.Login.lang', 'en'));
52828         Roo.state.Manager.set( this.realm + 'lang' , au.lang);
52829         this.switchLang(au.lang );
52830         
52831      
52832         // open system... - -on setyp..
52833         if (this.authUserId  < 0) {
52834             Roo.MessageBox.alert("Warning", 
52835                 "This is an open system - please set up a admin user with a password.");  
52836         }
52837          
52838         //Pman.onload(); // which should do nothing if it's a re-auth result...
52839         
52840              
52841     },
52842     
52843     startAuthCheck : function() // starter for timeout checking..
52844     {
52845         if (this.intervalID) { // timer already in place...
52846             return false;
52847         }
52848         var _this = this;
52849         this.intervalID =  window.setInterval(function() {
52850               _this.check(false);
52851             }, 120000); // every 120 secs = 2mins..
52852         
52853         
52854     },
52855          
52856     
52857     switchLang : function (lang) 
52858     {
52859         _T = typeof(_T) == 'undefined' ? false : _T;
52860           if (!_T || !lang.length) {
52861             return;
52862         }
52863         
52864         if (!_T && lang != 'en') {
52865             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
52866             return;
52867         }
52868         
52869         if (typeof(_T.en) == 'undefined') {
52870             _T.en = {};
52871             Roo.apply(_T.en, _T);
52872         }
52873         
52874         if (typeof(_T[lang]) == 'undefined') {
52875             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
52876             return;
52877         }
52878         
52879         
52880         Roo.apply(_T, _T[lang]);
52881         // just need to set the text values for everything...
52882         var _this = this;
52883         /* this will not work ...
52884         if (this.form) { 
52885             
52886                
52887             function formLabel(name, val) {
52888                 _this.form.findField(name).fieldEl.child('label').dom.innerHTML  = val;
52889             }
52890             
52891             formLabel('password', "Password"+':');
52892             formLabel('username', "Email Address"+':');
52893             formLabel('lang', "Language"+':');
52894             this.dialog.setTitle("Login");
52895             this.dialog.buttons[0].setText("Forgot Password");
52896             this.dialog.buttons[1].setText("Login");
52897         }
52898         */
52899         
52900         
52901     },
52902     
52903     
52904     title: "Login",
52905     modal: true,
52906     width:  350,
52907     //height: 230,
52908     height: 180,
52909     shadow: true,
52910     minWidth:200,
52911     minHeight:180,
52912     //proxyDrag: true,
52913     closable: false,
52914     draggable: false,
52915     collapsible: false,
52916     resizable: false,
52917     center: {  // needed??
52918         autoScroll:false,
52919         titlebar: false,
52920        // tabPosition: 'top',
52921         hideTabs: true,
52922         closeOnTab: true,
52923         alwaysShowTabs: false
52924     } ,
52925     listeners : {
52926         
52927         show  : function(dlg)
52928         {
52929             //console.log(this);
52930             this.form = this.layout.getRegion('center').activePanel.form;
52931             this.form.dialog = dlg;
52932             this.buttons[0].form = this.form;
52933             this.buttons[0].dialog = dlg;
52934             this.buttons[1].form = this.form;
52935             this.buttons[1].dialog = dlg;
52936            
52937            //this.resizeToLogo.defer(1000,this);
52938             // this is all related to resizing for logos..
52939             //var sz = Roo.get(Pman.Login.form.el.query('img')[0]).getSize();
52940            //// if (!sz) {
52941              //   this.resizeToLogo.defer(1000,this);
52942              //   return;
52943            // }
52944             //var w = Ext.lib.Dom.getViewWidth() - 100;
52945             //var h = Ext.lib.Dom.getViewHeight() - 100;
52946             //this.resizeTo(Math.max(350, Math.min(sz.width + 30, w)),Math.min(sz.height+200, h));
52947             //this.center();
52948             if (this.disabled) {
52949                 this.hide();
52950                 return;
52951             }
52952             
52953             if (this.user.id < 0) { // used for inital setup situations.
52954                 return;
52955             }
52956             
52957             if (this.intervalID) {
52958                 // remove the timer
52959                 window.clearInterval(this.intervalID);
52960                 this.intervalID = false;
52961             }
52962             
52963             
52964             if (Roo.get('loading')) {
52965                 Roo.get('loading').remove();
52966             }
52967             if (Roo.get('loading-mask')) {
52968                 Roo.get('loading-mask').hide();
52969             }
52970             
52971             //incomming._node = tnode;
52972             this.form.reset();
52973             //this.dialog.modal = !modal;
52974             //this.dialog.show();
52975             this.el.unmask(); 
52976             
52977             
52978             this.form.setValues({
52979                 'username' : Roo.state.Manager.get(this.realm + '.username', ''),
52980                 'lang' : Roo.state.Manager.get(this.realm + '.lang', 'en')
52981             });
52982             
52983             this.switchLang(Roo.state.Manager.get(this.realm + '.lang', 'en'));
52984             if (this.form.findField('username').getValue().length > 0 ){
52985                 this.form.findField('password').focus();
52986             } else {
52987                this.form.findField('username').focus();
52988             }
52989     
52990         }
52991     },
52992     items : [
52993          {
52994        
52995             xtype : 'ContentPanel',
52996             xns : Roo,
52997             region: 'center',
52998             fitToFrame : true,
52999             
53000             items : [
53001     
53002                 {
53003                
53004                     xtype : 'Form',
53005                     xns : Roo.form,
53006                     labelWidth: 100,
53007                     style : 'margin: 10px;',
53008                     
53009                     listeners : {
53010                         actionfailed : function(f, act) {
53011                             // form can return { errors: .... }
53012                                 
53013                             //act.result.errors // invalid form element list...
53014                             //act.result.errorMsg// invalid form element list...
53015                             
53016                             this.dialog.el.unmask();
53017                             Roo.MessageBox.alert("Error", act.result.errorMsg ? act.result.errorMsg : 
53018                                         "Login failed - communication error - try again.");
53019                                       
53020                         },
53021                         actioncomplete: function(re, act) {
53022                              
53023                             Roo.state.Manager.set(
53024                                 this.dialog.realm + '.username',  
53025                                     this.findField('username').getValue()
53026                             );
53027                             Roo.state.Manager.set(
53028                                 this.dialog.realm + '.lang',  
53029                                 this.findField('lang').getValue() 
53030                             );
53031                             
53032                             this.dialog.fillAuth(act.result.data);
53033                               
53034                             this.dialog.hide();
53035                             
53036                             if (Roo.get('loading-mask')) {
53037                                 Roo.get('loading-mask').show();
53038                             }
53039                             Roo.XComponent.build();
53040                             
53041                              
53042                             
53043                         }
53044                     },
53045                     items : [
53046                         {
53047                             xtype : 'TextField',
53048                             xns : Roo.form,
53049                             fieldLabel: "Email Address",
53050                             name: 'username',
53051                             width:200,
53052                             autoCreate : {tag: "input", type: "text", size: "20"}
53053                         },
53054                         {
53055                             xtype : 'TextField',
53056                             xns : Roo.form,
53057                             fieldLabel: "Password",
53058                             inputType: 'password',
53059                             name: 'password',
53060                             width:200,
53061                             autoCreate : {tag: "input", type: "text", size: "20"},
53062                             listeners : {
53063                                 specialkey : function(e,ev) {
53064                                     if (ev.keyCode == 13) {
53065                                         this.form.dialog.el.mask("Logging in");
53066                                         this.form.doAction('submit', {
53067                                             url: this.form.dialog.url,
53068                                             method: this.form.dialog.method
53069                                         });
53070                                     }
53071                                 }
53072                             }  
53073                         },
53074                         {
53075                             xtype : 'ComboBox',
53076                             xns : Roo.form,
53077                             fieldLabel: "Language",
53078                             name : 'langdisp',
53079                             store: {
53080                                 xtype : 'SimpleStore',
53081                                 fields: ['lang', 'ldisp'],
53082                                 data : [
53083                                     [ 'en', 'English' ],
53084                                     [ 'zh_HK' , '\u7E41\u4E2D' ],
53085                                     [ 'zh_CN', '\u7C21\u4E2D' ]
53086                                 ]
53087                             },
53088                             
53089                             valueField : 'lang',
53090                             hiddenName:  'lang',
53091                             width: 200,
53092                             displayField:'ldisp',
53093                             typeAhead: false,
53094                             editable: false,
53095                             mode: 'local',
53096                             triggerAction: 'all',
53097                             emptyText:'Select a Language...',
53098                             selectOnFocus:true,
53099                             listeners : {
53100                                 select :  function(cb, rec, ix) {
53101                                     this.form.switchLang(rec.data.lang);
53102                                 }
53103                             }
53104                         
53105                         }
53106                     ]
53107                 }
53108                   
53109                 
53110             ]
53111         }
53112     ],
53113     buttons : [
53114         {
53115             xtype : 'Button',
53116             xns : 'Roo',
53117             text : "Forgot Password",
53118             listeners : {
53119                 click : function() {
53120                     //console.log(this);
53121                     var n = this.form.findField('username').getValue();
53122                     if (!n.length) {
53123                         Roo.MessageBox.alert("Error", "Fill in your email address");
53124                         return;
53125                     }
53126                     Roo.Ajax.request({
53127                         url: this.dialog.url,
53128                         params: {
53129                             passwordRequest: n
53130                         },
53131                         method: this.dialog.method,
53132                         success:  function(response, opts)  {  // check successfull...
53133                         
53134                             var res = this.dialog.processResponse(response);
53135                             if (!res.success) { // error!
53136                                Roo.MessageBox.alert("Error" ,
53137                                     res.errorMsg ? res.errorMsg  : "Problem Requesting Password Reset");
53138                                return;
53139                             }
53140                             Roo.MessageBox.alert("Notice" ,
53141                                 "Please check you email for the Password Reset message");
53142                         },
53143                         failure : function() {
53144                             Roo.MessageBox.alert("Error" , "Problem Requesting Password Reset");
53145                         }
53146                         
53147                     });
53148                 }
53149             }
53150         },
53151         {
53152             xtype : 'Button',
53153             xns : 'Roo',
53154             text : "Login",
53155             listeners : {
53156                 
53157                 click : function () {
53158                         
53159                     this.dialog.el.mask("Logging in");
53160                     this.form.doAction('submit', {
53161                             url: this.dialog.url,
53162                             method: this.dialog.method
53163                     });
53164                 }
53165             }
53166         }
53167     ]
53168   
53169   
53170 })
53171  
53172
53173
53174