Revert "Roo/form/BasicForm.js"
[roojs1] / roojs-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12
13
14
15
16 // for old browsers
17 window["undefined"] = window["undefined"];
18
19 /**
20  * @class Roo
21  * Roo core utilities and functions.
22  * @singleton
23  */
24 var Roo = {}; 
25 /**
26  * Copies all the properties of config to obj.
27  * @param {Object} obj The receiver of the properties
28  * @param {Object} config The source of the properties
29  * @param {Object} defaults A different object that will also be applied for default values
30  * @return {Object} returns obj
31  * @member Roo apply
32  */
33
34  
35 Roo.apply = function(o, c, defaults){
36     if(defaults){
37         // no "this" reference for friendly out of scope calls
38         Roo.apply(o, defaults);
39     }
40     if(o && c && typeof c == 'object'){
41         for(var p in c){
42             o[p] = c[p];
43         }
44     }
45     return o;
46 };
47
48
49 (function(){
50     var idSeed = 0;
51     var ua = navigator.userAgent.toLowerCase();
52
53     var isStrict = document.compatMode == "CSS1Compat",
54         isOpera = ua.indexOf("opera") > -1,
55         isSafari = (/webkit|khtml/).test(ua),
56         isIE = ua.indexOf("msie") > -1,
57         isIE7 = ua.indexOf("msie 7") > -1,
58         isGecko = !isSafari && ua.indexOf("gecko") > -1,
59         isBorderBox = isIE && !isStrict,
60         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
61         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
62         isLinux = (ua.indexOf("linux") != -1),
63         isSecure = window.location.href.toLowerCase().indexOf("https") === 0;
64
65     // remove css image flicker
66         if(isIE && !isIE7){
67         try{
68             document.execCommand("BackgroundImageCache", false, true);
69         }catch(e){}
70     }
71
72     Roo.apply(Roo, {
73         /**
74          * True if the browser is in strict mode
75          * @type Boolean
76          */
77         isStrict : isStrict,
78         /**
79          * True if the page is running over SSL
80          * @type Boolean
81          */
82         isSecure : isSecure,
83         /**
84          * True when the document is fully initialized and ready for action
85          * @type Boolean
86          */
87         isReady : false,
88         /**
89          * Turn on debugging output (currently only the factory uses this)
90          * @type Boolean
91          */
92         
93         debug: false,
94
95         /**
96          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
97          * @type Boolean
98          */
99         enableGarbageCollector : true,
100
101         /**
102          * True to automatically purge event listeners after uncaching an element (defaults to false).
103          * Note: this only happens if enableGarbageCollector is true.
104          * @type Boolean
105          */
106         enableListenerCollection:false,
107
108         /**
109          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
110          * the IE insecure content warning (defaults to javascript:false).
111          * @type String
112          */
113         SSL_SECURE_URL : "javascript:false",
114
115         /**
116          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
117          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
118          * @type String
119          */
120         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
121
122         emptyFn : function(){},
123
124         /**
125          * Copies all the properties of config to obj if they don't already exist.
126          * @param {Object} obj The receiver of the properties
127          * @param {Object} config The source of the properties
128          * @return {Object} returns obj
129          */
130         applyIf : function(o, c){
131             if(o && c){
132                 for(var p in c){
133                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
134                 }
135             }
136             return o;
137         },
138
139         /**
140          * Applies event listeners to elements by selectors when the document is ready.
141          * The event name is specified with an @ suffix.
142 <pre><code>
143 Roo.addBehaviors({
144    // add a listener for click on all anchors in element with id foo
145    '#foo a@click' : function(e, t){
146        // do something
147    },
148
149    // add the same listener to multiple selectors (separated by comma BEFORE the @)
150    '#foo a, #bar span.some-class@mouseover' : function(){
151        // do something
152    }
153 });
154 </code></pre>
155          * @param {Object} obj The list of behaviors to apply
156          */
157         addBehaviors : function(o){
158             if(!Roo.isReady){
159                 Roo.onReady(function(){
160                     Roo.addBehaviors(o);
161                 });
162                 return;
163             }
164             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
165             for(var b in o){
166                 var parts = b.split('@');
167                 if(parts[1]){ // for Object prototype breakers
168                     var s = parts[0];
169                     if(!cache[s]){
170                         cache[s] = Roo.select(s);
171                     }
172                     cache[s].on(parts[1], o[b]);
173                 }
174             }
175             cache = null;
176         },
177
178         /**
179          * Generates unique ids. If the element already has an id, it is unchanged
180          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
181          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
182          * @return {String} The generated Id.
183          */
184         id : function(el, prefix){
185             prefix = prefix || "roo-gen";
186             el = Roo.getDom(el);
187             var id = prefix + (++idSeed);
188             return el ? (el.id ? el.id : (el.id = id)) : id;
189         },
190          
191        
192         /**
193          * Extends one class with another class and optionally overrides members with the passed literal. This class
194          * also adds the function "override()" to the class that can be used to override
195          * members on an instance.
196          * @param {Object} subclass The class inheriting the functionality
197          * @param {Object} superclass The class being extended
198          * @param {Object} overrides (optional) A literal with members
199          * @method extend
200          */
201         extend : function(){
202             // inline overrides
203             var io = function(o){
204                 for(var m in o){
205                     this[m] = o[m];
206                 }
207             };
208             return function(sb, sp, overrides){
209                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
210                     overrides = sp;
211                     sp = sb;
212                     sb = function(){sp.apply(this, arguments);};
213                 }
214                 var F = function(){}, sbp, spp = sp.prototype;
215                 F.prototype = spp;
216                 sbp = sb.prototype = new F();
217                 sbp.constructor=sb;
218                 sb.superclass=spp;
219                 
220                 if(spp.constructor == Object.prototype.constructor){
221                     spp.constructor=sp;
222                    
223                 }
224                 
225                 sb.override = function(o){
226                     Roo.override(sb, o);
227                 };
228                 sbp.override = io;
229                 Roo.override(sb, overrides);
230                 return sb;
231             };
232         }(),
233
234         /**
235          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
236          * Usage:<pre><code>
237 Roo.override(MyClass, {
238     newMethod1: function(){
239         // etc.
240     },
241     newMethod2: function(foo){
242         // etc.
243     }
244 });
245  </code></pre>
246          * @param {Object} origclass The class to override
247          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
248          * containing one or more methods.
249          * @method override
250          */
251         override : function(origclass, overrides){
252             if(overrides){
253                 var p = origclass.prototype;
254                 for(var method in overrides){
255                     p[method] = overrides[method];
256                 }
257             }
258         },
259         /**
260          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
261          * <pre><code>
262 Roo.namespace('Company', 'Company.data');
263 Company.Widget = function() { ... }
264 Company.data.CustomStore = function(config) { ... }
265 </code></pre>
266          * @param {String} namespace1
267          * @param {String} namespace2
268          * @param {String} etc
269          * @method namespace
270          */
271         namespace : function(){
272             var a=arguments, o=null, i, j, d, rt;
273             for (i=0; i<a.length; ++i) {
274                 d=a[i].split(".");
275                 rt = d[0];
276                 /** eval:var:o */
277                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
278                 for (j=1; j<d.length; ++j) {
279                     o[d[j]]=o[d[j]] || {};
280                     o=o[d[j]];
281                 }
282             }
283         },
284         /**
285          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
286          * <pre><code>
287 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
288 Roo.factory(conf, Roo.data);
289 </code></pre>
290          * @param {String} classname
291          * @param {String} namespace (optional)
292          * @method factory
293          */
294          
295         factory : function(c, ns)
296         {
297             // no xtype, no ns or c.xns - or forced off by c.xns
298             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
299                 return c;
300             }
301             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
302             if (c.constructor == ns[c.xtype]) {// already created...
303                 return c;
304             }
305             if (ns[c.xtype]) {
306                 if (Roo.debug) Roo.log("Roo.Factory(" + c.xtype + ")");
307                 var ret = new ns[c.xtype](c);
308                 ret.xns = false;
309                 return ret;
310             }
311             c.xns = false; // prevent recursion..
312             return c;
313         },
314          /**
315          * Logs to console if it can.
316          *
317          * @param {String|Object} string
318          * @method log
319          */
320         log : function(s)
321         {
322             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
323                 return; // alerT?
324             }
325             console.log(s);
326             
327         },
328         /**
329          * Takes an object and converts it to an encoded URL. e.g. Roo.urlEncode({foo: 1, bar: 2}); would return "foo=1&bar=2".  Optionally, property values can be arrays, instead of keys and the resulting string that's returned will contain a name/value pair for each array value.
330          * @param {Object} o
331          * @return {String}
332          */
333         urlEncode : function(o){
334             if(!o){
335                 return "";
336             }
337             var buf = [];
338             for(var key in o){
339                 var ov = o[key], k = Roo.encodeURIComponent(key);
340                 var type = typeof ov;
341                 if(type == 'undefined'){
342                     buf.push(k, "=&");
343                 }else if(type != "function" && type != "object"){
344                     buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
345                 }else if(ov instanceof Array){
346                     if (ov.length) {
347                             for(var i = 0, len = ov.length; i < len; i++) {
348                                 buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
349                             }
350                         } else {
351                             buf.push(k, "=&");
352                         }
353                 }
354             }
355             buf.pop();
356             return buf.join("");
357         },
358          /**
359          * Safe version of encodeURIComponent
360          * @param {String} data 
361          * @return {String} 
362          */
363         
364         encodeURIComponent : function (data)
365         {
366             try {
367                 return encodeURIComponent(data);
368             } catch(e) {} // should be an uri encode error.
369             
370             if (data == '' || data == null){
371                return '';
372             }
373             // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
374             function nibble_to_hex(nibble){
375                 var chars = '0123456789ABCDEF';
376                 return chars.charAt(nibble);
377             }
378             data = data.toString();
379             var buffer = '';
380             for(var i=0; i<data.length; i++){
381                 var c = data.charCodeAt(i);
382                 var bs = new Array();
383                 if (c > 0x10000){
384                         // 4 bytes
385                     bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
386                     bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
387                     bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
388                     bs[3] = 0x80 | (c & 0x3F);
389                 }else if (c > 0x800){
390                          // 3 bytes
391                     bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
392                     bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
393                     bs[2] = 0x80 | (c & 0x3F);
394                 }else if (c > 0x80){
395                        // 2 bytes
396                     bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
397                     bs[1] = 0x80 | (c & 0x3F);
398                 }else{
399                         // 1 byte
400                     bs[0] = c;
401                 }
402                 for(var j=0; j<bs.length; j++){
403                     var b = bs[j];
404                     var hex = nibble_to_hex((b & 0xF0) >>> 4) 
405                             + nibble_to_hex(b &0x0F);
406                     buffer += '%'+hex;
407                }
408             }
409             return buffer;    
410              
411         },
412
413         /**
414          * Takes an encoded URL and and converts it to an object. e.g. Roo.urlDecode("foo=1&bar=2"); would return {foo: 1, bar: 2} or Roo.urlDecode("foo=1&bar=2&bar=3&bar=4", true); would return {foo: 1, bar: [2, 3, 4]}.
415          * @param {String} string
416          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
417          * @return {Object} A literal with members
418          */
419         urlDecode : function(string, overwrite){
420             if(!string || !string.length){
421                 return {};
422             }
423             var obj = {};
424             var pairs = string.split('&');
425             var pair, name, value;
426             for(var i = 0, len = pairs.length; i < len; i++){
427                 pair = pairs[i].split('=');
428                 name = decodeURIComponent(pair[0]);
429                 value = decodeURIComponent(pair[1]);
430                 if(overwrite !== true){
431                     if(typeof obj[name] == "undefined"){
432                         obj[name] = value;
433                     }else if(typeof obj[name] == "string"){
434                         obj[name] = [obj[name]];
435                         obj[name].push(value);
436                     }else{
437                         obj[name].push(value);
438                     }
439                 }else{
440                     obj[name] = value;
441                 }
442             }
443             return obj;
444         },
445
446         /**
447          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
448          * passed array is not really an array, your function is called once with it.
449          * The supplied function is called with (Object item, Number index, Array allItems).
450          * @param {Array/NodeList/Mixed} array
451          * @param {Function} fn
452          * @param {Object} scope
453          */
454         each : function(array, fn, scope){
455             if(typeof array.length == "undefined" || typeof array == "string"){
456                 array = [array];
457             }
458             for(var i = 0, len = array.length; i < len; i++){
459                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
460             }
461         },
462
463         // deprecated
464         combine : function(){
465             var as = arguments, l = as.length, r = [];
466             for(var i = 0; i < l; i++){
467                 var a = as[i];
468                 if(a instanceof Array){
469                     r = r.concat(a);
470                 }else if(a.length !== undefined && !a.substr){
471                     r = r.concat(Array.prototype.slice.call(a, 0));
472                 }else{
473                     r.push(a);
474                 }
475             }
476             return r;
477         },
478
479         /**
480          * Escapes the passed string for use in a regular expression
481          * @param {String} str
482          * @return {String}
483          */
484         escapeRe : function(s) {
485             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
486         },
487
488         // internal
489         callback : function(cb, scope, args, delay){
490             if(typeof cb == "function"){
491                 if(delay){
492                     cb.defer(delay, scope, args || []);
493                 }else{
494                     cb.apply(scope, args || []);
495                 }
496             }
497         },
498
499         /**
500          * Return the dom node for the passed string (id), dom node, or Roo.Element
501          * @param {String/HTMLElement/Roo.Element} el
502          * @return HTMLElement
503          */
504         getDom : function(el){
505             if(!el){
506                 return null;
507             }
508             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
509         },
510
511         /**
512         * Shorthand for {@link Roo.ComponentMgr#get}
513         * @param {String} id
514         * @return Roo.Component
515         */
516         getCmp : function(id){
517             return Roo.ComponentMgr.get(id);
518         },
519          
520         num : function(v, defaultValue){
521             if(typeof v != 'number'){
522                 return defaultValue;
523             }
524             return v;
525         },
526
527         destroy : function(){
528             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
529                 var as = a[i];
530                 if(as){
531                     if(as.dom){
532                         as.removeAllListeners();
533                         as.remove();
534                         continue;
535                     }
536                     if(typeof as.purgeListeners == 'function'){
537                         as.purgeListeners();
538                     }
539                     if(typeof as.destroy == 'function'){
540                         as.destroy();
541                     }
542                 }
543             }
544         },
545
546         // inpired by a similar function in mootools library
547         /**
548          * Returns the type of object that is passed in. If the object passed in is null or undefined it
549          * return false otherwise it returns one of the following values:<ul>
550          * <li><b>string</b>: If the object passed is a string</li>
551          * <li><b>number</b>: If the object passed is a number</li>
552          * <li><b>boolean</b>: If the object passed is a boolean value</li>
553          * <li><b>function</b>: If the object passed is a function reference</li>
554          * <li><b>object</b>: If the object passed is an object</li>
555          * <li><b>array</b>: If the object passed is an array</li>
556          * <li><b>regexp</b>: If the object passed is a regular expression</li>
557          * <li><b>element</b>: If the object passed is a DOM Element</li>
558          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
559          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
560          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
561          * @param {Mixed} object
562          * @return {String}
563          */
564         type : function(o){
565             if(o === undefined || o === null){
566                 return false;
567             }
568             if(o.htmlElement){
569                 return 'element';
570             }
571             var t = typeof o;
572             if(t == 'object' && o.nodeName) {
573                 switch(o.nodeType) {
574                     case 1: return 'element';
575                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
576                 }
577             }
578             if(t == 'object' || t == 'function') {
579                 switch(o.constructor) {
580                     case Array: return 'array';
581                     case RegExp: return 'regexp';
582                 }
583                 if(typeof o.length == 'number' && typeof o.item == 'function') {
584                     return 'nodelist';
585                 }
586             }
587             return t;
588         },
589
590         /**
591          * Returns true if the passed value is null, undefined or an empty string (optional).
592          * @param {Mixed} value The value to test
593          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
594          * @return {Boolean}
595          */
596         isEmpty : function(v, allowBlank){
597             return v === null || v === undefined || (!allowBlank ? v === '' : false);
598         },
599         
600         /** @type Boolean */
601         isOpera : isOpera,
602         /** @type Boolean */
603         isSafari : isSafari,
604         /** @type Boolean */
605         isIE : isIE,
606         /** @type Boolean */
607         isIE7 : isIE7,
608         /** @type Boolean */
609         isGecko : isGecko,
610         /** @type Boolean */
611         isBorderBox : isBorderBox,
612         /** @type Boolean */
613         isWindows : isWindows,
614         /** @type Boolean */
615         isLinux : isLinux,
616         /** @type Boolean */
617         isMac : isMac,
618
619         /**
620          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
621          * you may want to set this to true.
622          * @type Boolean
623          */
624         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
625         
626         
627                 
628         /**
629          * Selects a single element as a Roo Element
630          * This is about as close as you can get to jQuery's $('do crazy stuff')
631          * @param {String} selector The selector/xpath query
632          * @param {Node} root (optional) The start of the query (defaults to document).
633          * @return {Roo.Element}
634          */
635         selectNode : function(selector, root) 
636         {
637             var node = Roo.DomQuery.selectNode(selector,root);
638             return node ? Roo.get(node) : new Roo.Element(false);
639         }
640         
641     });
642
643
644 })();
645
646 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
647                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout", "Roo.app", "Roo.ux");
648 /*
649  * Based on:
650  * Ext JS Library 1.1.1
651  * Copyright(c) 2006-2007, Ext JS, LLC.
652  *
653  * Originally Released Under LGPL - original licence link has changed is not relivant.
654  *
655  * Fork - LGPL
656  * <script type="text/javascript">
657  */
658
659 (function() {    
660     // wrappedn so fnCleanup is not in global scope...
661     if(Roo.isIE) {
662         function fnCleanUp() {
663             var p = Function.prototype;
664             delete p.createSequence;
665             delete p.defer;
666             delete p.createDelegate;
667             delete p.createCallback;
668             delete p.createInterceptor;
669
670             window.detachEvent("onunload", fnCleanUp);
671         }
672         window.attachEvent("onunload", fnCleanUp);
673     }
674 })();
675
676
677 /**
678  * @class Function
679  * These functions are available on every Function object (any JavaScript function).
680  */
681 Roo.apply(Function.prototype, {
682      /**
683      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
684      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
685      * Will create a function that is bound to those 2 args.
686      * @return {Function} The new function
687     */
688     createCallback : function(/*args...*/){
689         // make args available, in function below
690         var args = arguments;
691         var method = this;
692         return function() {
693             return method.apply(window, args);
694         };
695     },
696
697     /**
698      * Creates a delegate (callback) that sets the scope to obj.
699      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
700      * Will create a function that is automatically scoped to this.
701      * @param {Object} obj (optional) The object for which the scope is set
702      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
703      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
704      *                                             if a number the args are inserted at the specified position
705      * @return {Function} The new function
706      */
707     createDelegate : function(obj, args, appendArgs){
708         var method = this;
709         return function() {
710             var callArgs = args || arguments;
711             if(appendArgs === true){
712                 callArgs = Array.prototype.slice.call(arguments, 0);
713                 callArgs = callArgs.concat(args);
714             }else if(typeof appendArgs == "number"){
715                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
716                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
717                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
718             }
719             return method.apply(obj || window, callArgs);
720         };
721     },
722
723     /**
724      * Calls this function after the number of millseconds specified.
725      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
726      * @param {Object} obj (optional) The object for which the scope is set
727      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
728      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
729      *                                             if a number the args are inserted at the specified position
730      * @return {Number} The timeout id that can be used with clearTimeout
731      */
732     defer : function(millis, obj, args, appendArgs){
733         var fn = this.createDelegate(obj, args, appendArgs);
734         if(millis){
735             return setTimeout(fn, millis);
736         }
737         fn();
738         return 0;
739     },
740     /**
741      * Create a combined function call sequence of the original function + the passed function.
742      * The resulting function returns the results of the original function.
743      * The passed fcn is called with the parameters of the original function
744      * @param {Function} fcn The function to sequence
745      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
746      * @return {Function} The new function
747      */
748     createSequence : function(fcn, scope){
749         if(typeof fcn != "function"){
750             return this;
751         }
752         var method = this;
753         return function() {
754             var retval = method.apply(this || window, arguments);
755             fcn.apply(scope || this || window, arguments);
756             return retval;
757         };
758     },
759
760     /**
761      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
762      * The resulting function returns the results of the original function.
763      * The passed fcn is called with the parameters of the original function.
764      * @addon
765      * @param {Function} fcn The function to call before the original
766      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
767      * @return {Function} The new function
768      */
769     createInterceptor : function(fcn, scope){
770         if(typeof fcn != "function"){
771             return this;
772         }
773         var method = this;
774         return function() {
775             fcn.target = this;
776             fcn.method = method;
777             if(fcn.apply(scope || this || window, arguments) === false){
778                 return;
779             }
780             return method.apply(this || window, arguments);
781         };
782     }
783 });
784 /*
785  * Based on:
786  * Ext JS Library 1.1.1
787  * Copyright(c) 2006-2007, Ext JS, LLC.
788  *
789  * Originally Released Under LGPL - original licence link has changed is not relivant.
790  *
791  * Fork - LGPL
792  * <script type="text/javascript">
793  */
794
795 Roo.applyIf(String, {
796     
797     /** @scope String */
798     
799     /**
800      * Escapes the passed string for ' and \
801      * @param {String} string The string to escape
802      * @return {String} The escaped string
803      * @static
804      */
805     escape : function(string) {
806         return string.replace(/('|\\)/g, "\\$1");
807     },
808
809     /**
810      * Pads the left side of a string with a specified character.  This is especially useful
811      * for normalizing number and date strings.  Example usage:
812      * <pre><code>
813 var s = String.leftPad('123', 5, '0');
814 // s now contains the string: '00123'
815 </code></pre>
816      * @param {String} string The original string
817      * @param {Number} size The total length of the output string
818      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
819      * @return {String} The padded string
820      * @static
821      */
822     leftPad : function (val, size, ch) {
823         var result = new String(val);
824         if(ch === null || ch === undefined || ch === '') {
825             ch = " ";
826         }
827         while (result.length < size) {
828             result = ch + result;
829         }
830         return result;
831     },
832
833     /**
834      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
835      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
836      * <pre><code>
837 var cls = 'my-class', text = 'Some text';
838 var s = String.format('<div class="{0}">{1}</div>', cls, text);
839 // s now contains the string: '<div class="my-class">Some text</div>'
840 </code></pre>
841      * @param {String} string The tokenized string to be formatted
842      * @param {String} value1 The value to replace token {0}
843      * @param {String} value2 Etc...
844      * @return {String} The formatted string
845      * @static
846      */
847     format : function(format){
848         var args = Array.prototype.slice.call(arguments, 1);
849         return format.replace(/\{(\d+)\}/g, function(m, i){
850             return Roo.util.Format.htmlEncode(args[i]);
851         });
852     }
853 });
854
855 /**
856  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
857  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
858  * they are already different, the first value passed in is returned.  Note that this method returns the new value
859  * but does not change the current string.
860  * <pre><code>
861 // alternate sort directions
862 sort = sort.toggle('ASC', 'DESC');
863
864 // instead of conditional logic:
865 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
866 </code></pre>
867  * @param {String} value The value to compare to the current string
868  * @param {String} other The new value to use if the string already equals the first value passed in
869  * @return {String} The new value
870  */
871  
872 String.prototype.toggle = function(value, other){
873     return this == value ? other : value;
874 };/*
875  * Based on:
876  * Ext JS Library 1.1.1
877  * Copyright(c) 2006-2007, Ext JS, LLC.
878  *
879  * Originally Released Under LGPL - original licence link has changed is not relivant.
880  *
881  * Fork - LGPL
882  * <script type="text/javascript">
883  */
884
885  /**
886  * @class Number
887  */
888 Roo.applyIf(Number.prototype, {
889     /**
890      * Checks whether or not the current number is within a desired range.  If the number is already within the
891      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
892      * exceeded.  Note that this method returns the constrained value but does not change the current number.
893      * @param {Number} min The minimum number in the range
894      * @param {Number} max The maximum number in the range
895      * @return {Number} The constrained value if outside the range, otherwise the current value
896      */
897     constrain : function(min, max){
898         return Math.min(Math.max(this, min), max);
899     }
900 });/*
901  * Based on:
902  * Ext JS Library 1.1.1
903  * Copyright(c) 2006-2007, Ext JS, LLC.
904  *
905  * Originally Released Under LGPL - original licence link has changed is not relivant.
906  *
907  * Fork - LGPL
908  * <script type="text/javascript">
909  */
910  /**
911  * @class Array
912  */
913 Roo.applyIf(Array.prototype, {
914     /**
915      * Checks whether or not the specified object exists in the array.
916      * @param {Object} o The object to check for
917      * @return {Number} The index of o in the array (or -1 if it is not found)
918      */
919     indexOf : function(o){
920        for (var i = 0, len = this.length; i < len; i++){
921               if(this[i] == o) return i;
922        }
923            return -1;
924     },
925
926     /**
927      * Removes the specified object from the array.  If the object is not found nothing happens.
928      * @param {Object} o The object to remove
929      */
930     remove : function(o){
931        var index = this.indexOf(o);
932        if(index != -1){
933            this.splice(index, 1);
934        }
935     },
936     /**
937      * Map (JS 1.6 compatibility)
938      * @param {Function} function  to call
939      */
940     map : function(fun )
941     {
942         var len = this.length >>> 0;
943         if (typeof fun != "function")
944             throw new TypeError();
945
946         var res = new Array(len);
947         var thisp = arguments[1];
948         for (var i = 0; i < len; i++)
949         {
950             if (i in this)
951                 res[i] = fun.call(thisp, this[i], i, this);
952         }
953
954         return res;
955     }
956     
957 });
958
959
960  /*
961  * Based on:
962  * Ext JS Library 1.1.1
963  * Copyright(c) 2006-2007, Ext JS, LLC.
964  *
965  * Originally Released Under LGPL - original licence link has changed is not relivant.
966  *
967  * Fork - LGPL
968  * <script type="text/javascript">
969  */
970
971 /**
972  * @class Date
973  *
974  * The date parsing and format syntax is a subset of
975  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
976  * supported will provide results equivalent to their PHP versions.
977  *
978  * Following is the list of all currently supported formats:
979  *<pre>
980 Sample date:
981 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
982
983 Format  Output      Description
984 ------  ----------  --------------------------------------------------------------
985   d      10         Day of the month, 2 digits with leading zeros
986   D      Wed        A textual representation of a day, three letters
987   j      10         Day of the month without leading zeros
988   l      Wednesday  A full textual representation of the day of the week
989   S      th         English ordinal day of month suffix, 2 chars (use with j)
990   w      3          Numeric representation of the day of the week
991   z      9          The julian date, or day of the year (0-365)
992   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
993   F      January    A full textual representation of the month
994   m      01         Numeric representation of a month, with leading zeros
995   M      Jan        Month name abbreviation, three letters
996   n      1          Numeric representation of a month, without leading zeros
997   t      31         Number of days in the given month
998   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
999   Y      2007       A full numeric representation of a year, 4 digits
1000   y      07         A two digit representation of a year
1001   a      pm         Lowercase Ante meridiem and Post meridiem
1002   A      PM         Uppercase Ante meridiem and Post meridiem
1003   g      3          12-hour format of an hour without leading zeros
1004   G      15         24-hour format of an hour without leading zeros
1005   h      03         12-hour format of an hour with leading zeros
1006   H      15         24-hour format of an hour with leading zeros
1007   i      05         Minutes with leading zeros
1008   s      01         Seconds, with leading zeros
1009   O      -0600      Difference to Greenwich time (GMT) in hours
1010   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1011   T      CST        Timezone setting of the machine running the code
1012   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1013 </pre>
1014  *
1015  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1016  * <pre><code>
1017 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1018 document.write(dt.format('Y-m-d'));                         //2007-01-10
1019 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1020 document.write(dt.format('l, \\t\\he dS of F Y h:i:s A'));  //Wednesday, the 10th of January 2007 03:05:01 PM
1021  </code></pre>
1022  *
1023  * Here are some standard date/time patterns that you might find helpful.  They
1024  * are not part of the source of Date.js, but to use them you can simply copy this
1025  * block of code into any script that is included after Date.js and they will also become
1026  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1027  * <pre><code>
1028 Date.patterns = {
1029     ISO8601Long:"Y-m-d H:i:s",
1030     ISO8601Short:"Y-m-d",
1031     ShortDate: "n/j/Y",
1032     LongDate: "l, F d, Y",
1033     FullDateTime: "l, F d, Y g:i:s A",
1034     MonthDay: "F d",
1035     ShortTime: "g:i A",
1036     LongTime: "g:i:s A",
1037     SortableDateTime: "Y-m-d\\TH:i:s",
1038     UniversalSortableDateTime: "Y-m-d H:i:sO",
1039     YearMonth: "F, Y"
1040 };
1041 </code></pre>
1042  *
1043  * Example usage:
1044  * <pre><code>
1045 var dt = new Date();
1046 document.write(dt.format(Date.patterns.ShortDate));
1047  </code></pre>
1048  */
1049
1050 /*
1051  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1052  * They generate precompiled functions from date formats instead of parsing and
1053  * processing the pattern every time you format a date.  These functions are available
1054  * on every Date object (any javascript function).
1055  *
1056  * The original article and download are here:
1057  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1058  *
1059  */
1060  
1061  
1062  // was in core
1063 /**
1064  Returns the number of milliseconds between this date and date
1065  @param {Date} date (optional) Defaults to now
1066  @return {Number} The diff in milliseconds
1067  @member Date getElapsed
1068  */
1069 Date.prototype.getElapsed = function(date) {
1070         return Math.abs((date || new Date()).getTime()-this.getTime());
1071 };
1072 // was in date file..
1073
1074
1075 // private
1076 Date.parseFunctions = {count:0};
1077 // private
1078 Date.parseRegexes = [];
1079 // private
1080 Date.formatFunctions = {count:0};
1081
1082 // private
1083 Date.prototype.dateFormat = function(format) {
1084     if (Date.formatFunctions[format] == null) {
1085         Date.createNewFormat(format);
1086     }
1087     var func = Date.formatFunctions[format];
1088     return this[func]();
1089 };
1090
1091
1092 /**
1093  * Formats a date given the supplied format string
1094  * @param {String} format The format string
1095  * @return {String} The formatted date
1096  * @method
1097  */
1098 Date.prototype.format = Date.prototype.dateFormat;
1099
1100 // private
1101 Date.createNewFormat = function(format) {
1102     var funcName = "format" + Date.formatFunctions.count++;
1103     Date.formatFunctions[format] = funcName;
1104     var code = "Date.prototype." + funcName + " = function(){return ";
1105     var special = false;
1106     var ch = '';
1107     for (var i = 0; i < format.length; ++i) {
1108         ch = format.charAt(i);
1109         if (!special && ch == "\\") {
1110             special = true;
1111         }
1112         else if (special) {
1113             special = false;
1114             code += "'" + String.escape(ch) + "' + ";
1115         }
1116         else {
1117             code += Date.getFormatCode(ch);
1118         }
1119     }
1120     /** eval:var:zzzzzzzzzzzzz */
1121     eval(code.substring(0, code.length - 3) + ";}");
1122 };
1123
1124 // private
1125 Date.getFormatCode = function(character) {
1126     switch (character) {
1127     case "d":
1128         return "String.leftPad(this.getDate(), 2, '0') + ";
1129     case "D":
1130         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1131     case "j":
1132         return "this.getDate() + ";
1133     case "l":
1134         return "Date.dayNames[this.getDay()] + ";
1135     case "S":
1136         return "this.getSuffix() + ";
1137     case "w":
1138         return "this.getDay() + ";
1139     case "z":
1140         return "this.getDayOfYear() + ";
1141     case "W":
1142         return "this.getWeekOfYear() + ";
1143     case "F":
1144         return "Date.monthNames[this.getMonth()] + ";
1145     case "m":
1146         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1147     case "M":
1148         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1149     case "n":
1150         return "(this.getMonth() + 1) + ";
1151     case "t":
1152         return "this.getDaysInMonth() + ";
1153     case "L":
1154         return "(this.isLeapYear() ? 1 : 0) + ";
1155     case "Y":
1156         return "this.getFullYear() + ";
1157     case "y":
1158         return "('' + this.getFullYear()).substring(2, 4) + ";
1159     case "a":
1160         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1161     case "A":
1162         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1163     case "g":
1164         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1165     case "G":
1166         return "this.getHours() + ";
1167     case "h":
1168         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1169     case "H":
1170         return "String.leftPad(this.getHours(), 2, '0') + ";
1171     case "i":
1172         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1173     case "s":
1174         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1175     case "O":
1176         return "this.getGMTOffset() + ";
1177     case "P":
1178         return "this.getGMTColonOffset() + ";
1179     case "T":
1180         return "this.getTimezone() + ";
1181     case "Z":
1182         return "(this.getTimezoneOffset() * -60) + ";
1183     default:
1184         return "'" + String.escape(character) + "' + ";
1185     }
1186 };
1187
1188 /**
1189  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1190  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1191  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1192  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1193  * string or the parse operation will fail.
1194  * Example Usage:
1195 <pre><code>
1196 //dt = Fri May 25 2007 (current date)
1197 var dt = new Date();
1198
1199 //dt = Thu May 25 2006 (today's month/day in 2006)
1200 dt = Date.parseDate("2006", "Y");
1201
1202 //dt = Sun Jan 15 2006 (all date parts specified)
1203 dt = Date.parseDate("2006-1-15", "Y-m-d");
1204
1205 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1206 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1207 </code></pre>
1208  * @param {String} input The unparsed date as a string
1209  * @param {String} format The format the date is in
1210  * @return {Date} The parsed date
1211  * @static
1212  */
1213 Date.parseDate = function(input, format) {
1214     if (Date.parseFunctions[format] == null) {
1215         Date.createParser(format);
1216     }
1217     var func = Date.parseFunctions[format];
1218     return Date[func](input);
1219 };
1220 /**
1221  * @private
1222  */
1223 Date.createParser = function(format) {
1224     var funcName = "parse" + Date.parseFunctions.count++;
1225     var regexNum = Date.parseRegexes.length;
1226     var currentGroup = 1;
1227     Date.parseFunctions[format] = funcName;
1228
1229     var code = "Date." + funcName + " = function(input){\n"
1230         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1231         + "var d = new Date();\n"
1232         + "y = d.getFullYear();\n"
1233         + "m = d.getMonth();\n"
1234         + "d = d.getDate();\n"
1235         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1236         + "if (results && results.length > 0) {";
1237     var regex = "";
1238
1239     var special = false;
1240     var ch = '';
1241     for (var i = 0; i < format.length; ++i) {
1242         ch = format.charAt(i);
1243         if (!special && ch == "\\") {
1244             special = true;
1245         }
1246         else if (special) {
1247             special = false;
1248             regex += String.escape(ch);
1249         }
1250         else {
1251             var obj = Date.formatCodeToRegex(ch, currentGroup);
1252             currentGroup += obj.g;
1253             regex += obj.s;
1254             if (obj.g && obj.c) {
1255                 code += obj.c;
1256             }
1257         }
1258     }
1259
1260     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1261         + "{v = new Date(y, m, d, h, i, s);}\n"
1262         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1263         + "{v = new Date(y, m, d, h, i);}\n"
1264         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1265         + "{v = new Date(y, m, d, h);}\n"
1266         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1267         + "{v = new Date(y, m, d);}\n"
1268         + "else if (y >= 0 && m >= 0)\n"
1269         + "{v = new Date(y, m);}\n"
1270         + "else if (y >= 0)\n"
1271         + "{v = new Date(y);}\n"
1272         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1273         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1274         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1275         + ";}";
1276
1277     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1278     /** eval:var:zzzzzzzzzzzzz */
1279     eval(code);
1280 };
1281
1282 // private
1283 Date.formatCodeToRegex = function(character, currentGroup) {
1284     switch (character) {
1285     case "D":
1286         return {g:0,
1287         c:null,
1288         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1289     case "j":
1290         return {g:1,
1291             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1292             s:"(\\d{1,2})"}; // day of month without leading zeroes
1293     case "d":
1294         return {g:1,
1295             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1296             s:"(\\d{2})"}; // day of month with leading zeroes
1297     case "l":
1298         return {g:0,
1299             c:null,
1300             s:"(?:" + Date.dayNames.join("|") + ")"};
1301     case "S":
1302         return {g:0,
1303             c:null,
1304             s:"(?:st|nd|rd|th)"};
1305     case "w":
1306         return {g:0,
1307             c:null,
1308             s:"\\d"};
1309     case "z":
1310         return {g:0,
1311             c:null,
1312             s:"(?:\\d{1,3})"};
1313     case "W":
1314         return {g:0,
1315             c:null,
1316             s:"(?:\\d{2})"};
1317     case "F":
1318         return {g:1,
1319             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1320             s:"(" + Date.monthNames.join("|") + ")"};
1321     case "M":
1322         return {g:1,
1323             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1324             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1325     case "n":
1326         return {g:1,
1327             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1328             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1329     case "m":
1330         return {g:1,
1331             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1332             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1333     case "t":
1334         return {g:0,
1335             c:null,
1336             s:"\\d{1,2}"};
1337     case "L":
1338         return {g:0,
1339             c:null,
1340             s:"(?:1|0)"};
1341     case "Y":
1342         return {g:1,
1343             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1344             s:"(\\d{4})"};
1345     case "y":
1346         return {g:1,
1347             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1348                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1349             s:"(\\d{1,2})"};
1350     case "a":
1351         return {g:1,
1352             c:"if (results[" + currentGroup + "] == 'am') {\n"
1353                 + "if (h == 12) { h = 0; }\n"
1354                 + "} else { if (h < 12) { h += 12; }}",
1355             s:"(am|pm)"};
1356     case "A":
1357         return {g:1,
1358             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1359                 + "if (h == 12) { h = 0; }\n"
1360                 + "} else { if (h < 12) { h += 12; }}",
1361             s:"(AM|PM)"};
1362     case "g":
1363     case "G":
1364         return {g:1,
1365             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1366             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1367     case "h":
1368     case "H":
1369         return {g:1,
1370             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1371             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1372     case "i":
1373         return {g:1,
1374             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1375             s:"(\\d{2})"};
1376     case "s":
1377         return {g:1,
1378             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1379             s:"(\\d{2})"};
1380     case "O":
1381         return {g:1,
1382             c:[
1383                 "o = results[", currentGroup, "];\n",
1384                 "var sn = o.substring(0,1);\n", // get + / - sign
1385                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1386                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1387                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1388                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1389             ].join(""),
1390             s:"([+\-]\\d{4})"};
1391     case "P":
1392         return {g:1,
1393                 c:[
1394                    "o = results[", currentGroup, "];\n",
1395                    "var sn = o.substring(0,1);\n",
1396                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1397                    "var mn = o.substring(4,6) % 60;\n",
1398                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1399                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1400             ].join(""),
1401             s:"([+\-]\\d{4})"};
1402     case "T":
1403         return {g:0,
1404             c:null,
1405             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1406     case "Z":
1407         return {g:1,
1408             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1409                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1410             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1411     default:
1412         return {g:0,
1413             c:null,
1414             s:String.escape(character)};
1415     }
1416 };
1417
1418 /**
1419  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1420  * @return {String} The abbreviated timezone name (e.g. 'CST')
1421  */
1422 Date.prototype.getTimezone = function() {
1423     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1424 };
1425
1426 /**
1427  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1428  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1429  */
1430 Date.prototype.getGMTOffset = function() {
1431     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1432         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1433         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1434 };
1435
1436 /**
1437  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1438  * @return {String} 2-characters representing hours and 2-characters representing minutes
1439  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1440  */
1441 Date.prototype.getGMTColonOffset = function() {
1442         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1443                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1444                 + ":"
1445                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1446 }
1447
1448 /**
1449  * Get the numeric day number of the year, adjusted for leap year.
1450  * @return {Number} 0 through 364 (365 in leap years)
1451  */
1452 Date.prototype.getDayOfYear = function() {
1453     var num = 0;
1454     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1455     for (var i = 0; i < this.getMonth(); ++i) {
1456         num += Date.daysInMonth[i];
1457     }
1458     return num + this.getDate() - 1;
1459 };
1460
1461 /**
1462  * Get the string representation of the numeric week number of the year
1463  * (equivalent to the format specifier 'W').
1464  * @return {String} '00' through '52'
1465  */
1466 Date.prototype.getWeekOfYear = function() {
1467     // Skip to Thursday of this week
1468     var now = this.getDayOfYear() + (4 - this.getDay());
1469     // Find the first Thursday of the year
1470     var jan1 = new Date(this.getFullYear(), 0, 1);
1471     var then = (7 - jan1.getDay() + 4);
1472     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1473 };
1474
1475 /**
1476  * Whether or not the current date is in a leap year.
1477  * @return {Boolean} True if the current date is in a leap year, else false
1478  */
1479 Date.prototype.isLeapYear = function() {
1480     var year = this.getFullYear();
1481     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1482 };
1483
1484 /**
1485  * Get the first day of the current month, adjusted for leap year.  The returned value
1486  * is the numeric day index within the week (0-6) which can be used in conjunction with
1487  * the {@link #monthNames} array to retrieve the textual day name.
1488  * Example:
1489  *<pre><code>
1490 var dt = new Date('1/10/2007');
1491 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1492 </code></pre>
1493  * @return {Number} The day number (0-6)
1494  */
1495 Date.prototype.getFirstDayOfMonth = function() {
1496     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1497     return (day < 0) ? (day + 7) : day;
1498 };
1499
1500 /**
1501  * Get the last day of the current month, adjusted for leap year.  The returned value
1502  * is the numeric day index within the week (0-6) which can be used in conjunction with
1503  * the {@link #monthNames} array to retrieve the textual day name.
1504  * Example:
1505  *<pre><code>
1506 var dt = new Date('1/10/2007');
1507 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1508 </code></pre>
1509  * @return {Number} The day number (0-6)
1510  */
1511 Date.prototype.getLastDayOfMonth = function() {
1512     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1513     return (day < 0) ? (day + 7) : day;
1514 };
1515
1516
1517 /**
1518  * Get the first date of this date's month
1519  * @return {Date}
1520  */
1521 Date.prototype.getFirstDateOfMonth = function() {
1522     return new Date(this.getFullYear(), this.getMonth(), 1);
1523 };
1524
1525 /**
1526  * Get the last date of this date's month
1527  * @return {Date}
1528  */
1529 Date.prototype.getLastDateOfMonth = function() {
1530     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1531 };
1532 /**
1533  * Get the number of days in the current month, adjusted for leap year.
1534  * @return {Number} The number of days in the month
1535  */
1536 Date.prototype.getDaysInMonth = function() {
1537     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1538     return Date.daysInMonth[this.getMonth()];
1539 };
1540
1541 /**
1542  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1543  * @return {String} 'st, 'nd', 'rd' or 'th'
1544  */
1545 Date.prototype.getSuffix = function() {
1546     switch (this.getDate()) {
1547         case 1:
1548         case 21:
1549         case 31:
1550             return "st";
1551         case 2:
1552         case 22:
1553             return "nd";
1554         case 3:
1555         case 23:
1556             return "rd";
1557         default:
1558             return "th";
1559     }
1560 };
1561
1562 // private
1563 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1564
1565 /**
1566  * An array of textual month names.
1567  * Override these values for international dates, for example...
1568  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1569  * @type Array
1570  * @static
1571  */
1572 Date.monthNames =
1573    ["January",
1574     "February",
1575     "March",
1576     "April",
1577     "May",
1578     "June",
1579     "July",
1580     "August",
1581     "September",
1582     "October",
1583     "November",
1584     "December"];
1585
1586 /**
1587  * An array of textual day names.
1588  * Override these values for international dates, for example...
1589  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1590  * @type Array
1591  * @static
1592  */
1593 Date.dayNames =
1594    ["Sunday",
1595     "Monday",
1596     "Tuesday",
1597     "Wednesday",
1598     "Thursday",
1599     "Friday",
1600     "Saturday"];
1601
1602 // private
1603 Date.y2kYear = 50;
1604 // private
1605 Date.monthNumbers = {
1606     Jan:0,
1607     Feb:1,
1608     Mar:2,
1609     Apr:3,
1610     May:4,
1611     Jun:5,
1612     Jul:6,
1613     Aug:7,
1614     Sep:8,
1615     Oct:9,
1616     Nov:10,
1617     Dec:11};
1618
1619 /**
1620  * Creates and returns a new Date instance with the exact same date value as the called instance.
1621  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1622  * variable will also be changed.  When the intention is to create a new variable that will not
1623  * modify the original instance, you should create a clone.
1624  *
1625  * Example of correctly cloning a date:
1626  * <pre><code>
1627 //wrong way:
1628 var orig = new Date('10/1/2006');
1629 var copy = orig;
1630 copy.setDate(5);
1631 document.write(orig);  //returns 'Thu Oct 05 2006'!
1632
1633 //correct way:
1634 var orig = new Date('10/1/2006');
1635 var copy = orig.clone();
1636 copy.setDate(5);
1637 document.write(orig);  //returns 'Thu Oct 01 2006'
1638 </code></pre>
1639  * @return {Date} The new Date instance
1640  */
1641 Date.prototype.clone = function() {
1642         return new Date(this.getTime());
1643 };
1644
1645 /**
1646  * Clears any time information from this date
1647  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1648  @return {Date} this or the clone
1649  */
1650 Date.prototype.clearTime = function(clone){
1651     if(clone){
1652         return this.clone().clearTime();
1653     }
1654     this.setHours(0);
1655     this.setMinutes(0);
1656     this.setSeconds(0);
1657     this.setMilliseconds(0);
1658     return this;
1659 };
1660
1661 // private
1662 // safari setMonth is broken
1663 if(Roo.isSafari){
1664     Date.brokenSetMonth = Date.prototype.setMonth;
1665         Date.prototype.setMonth = function(num){
1666                 if(num <= -1){
1667                         var n = Math.ceil(-num);
1668                         var back_year = Math.ceil(n/12);
1669                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1670                         this.setFullYear(this.getFullYear() - back_year);
1671                         return Date.brokenSetMonth.call(this, month);
1672                 } else {
1673                         return Date.brokenSetMonth.apply(this, arguments);
1674                 }
1675         };
1676 }
1677
1678 /** Date interval constant 
1679 * @static 
1680 * @type String */
1681 Date.MILLI = "ms";
1682 /** Date interval constant 
1683 * @static 
1684 * @type String */
1685 Date.SECOND = "s";
1686 /** Date interval constant 
1687 * @static 
1688 * @type String */
1689 Date.MINUTE = "mi";
1690 /** Date interval constant 
1691 * @static 
1692 * @type String */
1693 Date.HOUR = "h";
1694 /** Date interval constant 
1695 * @static 
1696 * @type String */
1697 Date.DAY = "d";
1698 /** Date interval constant 
1699 * @static 
1700 * @type String */
1701 Date.MONTH = "mo";
1702 /** Date interval constant 
1703 * @static 
1704 * @type String */
1705 Date.YEAR = "y";
1706
1707 /**
1708  * Provides a convenient method of performing basic date arithmetic.  This method
1709  * does not modify the Date instance being called - it creates and returns
1710  * a new Date instance containing the resulting date value.
1711  *
1712  * Examples:
1713  * <pre><code>
1714 //Basic usage:
1715 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1716 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1717
1718 //Negative values will subtract correctly:
1719 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1720 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1721
1722 //You can even chain several calls together in one line!
1723 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1724 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1725  </code></pre>
1726  *
1727  * @param {String} interval   A valid date interval enum value
1728  * @param {Number} value      The amount to add to the current date
1729  * @return {Date} The new Date instance
1730  */
1731 Date.prototype.add = function(interval, value){
1732   var d = this.clone();
1733   if (!interval || value === 0) return d;
1734   switch(interval.toLowerCase()){
1735     case Date.MILLI:
1736       d.setMilliseconds(this.getMilliseconds() + value);
1737       break;
1738     case Date.SECOND:
1739       d.setSeconds(this.getSeconds() + value);
1740       break;
1741     case Date.MINUTE:
1742       d.setMinutes(this.getMinutes() + value);
1743       break;
1744     case Date.HOUR:
1745       d.setHours(this.getHours() + value);
1746       break;
1747     case Date.DAY:
1748       d.setDate(this.getDate() + value);
1749       break;
1750     case Date.MONTH:
1751       var day = this.getDate();
1752       if(day > 28){
1753           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1754       }
1755       d.setDate(day);
1756       d.setMonth(this.getMonth() + value);
1757       break;
1758     case Date.YEAR:
1759       d.setFullYear(this.getFullYear() + value);
1760       break;
1761   }
1762   return d;
1763 };
1764 /*
1765  * Based on:
1766  * Ext JS Library 1.1.1
1767  * Copyright(c) 2006-2007, Ext JS, LLC.
1768  *
1769  * Originally Released Under LGPL - original licence link has changed is not relivant.
1770  *
1771  * Fork - LGPL
1772  * <script type="text/javascript">
1773  */
1774
1775 Roo.lib.Dom = {
1776     getViewWidth : function(full) {
1777         return full ? this.getDocumentWidth() : this.getViewportWidth();
1778     },
1779
1780     getViewHeight : function(full) {
1781         return full ? this.getDocumentHeight() : this.getViewportHeight();
1782     },
1783
1784     getDocumentHeight: function() {
1785         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1786         return Math.max(scrollHeight, this.getViewportHeight());
1787     },
1788
1789     getDocumentWidth: function() {
1790         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1791         return Math.max(scrollWidth, this.getViewportWidth());
1792     },
1793
1794     getViewportHeight: function() {
1795         var height = self.innerHeight;
1796         var mode = document.compatMode;
1797
1798         if ((mode || Roo.isIE) && !Roo.isOpera) {
1799             height = (mode == "CSS1Compat") ?
1800                      document.documentElement.clientHeight :
1801                      document.body.clientHeight;
1802         }
1803
1804         return height;
1805     },
1806
1807     getViewportWidth: function() {
1808         var width = self.innerWidth;
1809         var mode = document.compatMode;
1810
1811         if (mode || Roo.isIE) {
1812             width = (mode == "CSS1Compat") ?
1813                     document.documentElement.clientWidth :
1814                     document.body.clientWidth;
1815         }
1816         return width;
1817     },
1818
1819     isAncestor : function(p, c) {
1820         p = Roo.getDom(p);
1821         c = Roo.getDom(c);
1822         if (!p || !c) {
1823             return false;
1824         }
1825
1826         if (p.contains && !Roo.isSafari) {
1827             return p.contains(c);
1828         } else if (p.compareDocumentPosition) {
1829             return !!(p.compareDocumentPosition(c) & 16);
1830         } else {
1831             var parent = c.parentNode;
1832             while (parent) {
1833                 if (parent == p) {
1834                     return true;
1835                 }
1836                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1837                     return false;
1838                 }
1839                 parent = parent.parentNode;
1840             }
1841             return false;
1842         }
1843     },
1844
1845     getRegion : function(el) {
1846         return Roo.lib.Region.getRegion(el);
1847     },
1848
1849     getY : function(el) {
1850         return this.getXY(el)[1];
1851     },
1852
1853     getX : function(el) {
1854         return this.getXY(el)[0];
1855     },
1856
1857     getXY : function(el) {
1858         var p, pe, b, scroll, bd = document.body;
1859         el = Roo.getDom(el);
1860         var fly = Roo.lib.AnimBase.fly;
1861         if (el.getBoundingClientRect) {
1862             b = el.getBoundingClientRect();
1863             scroll = fly(document).getScroll();
1864             return [b.left + scroll.left, b.top + scroll.top];
1865         }
1866         var x = 0, y = 0;
1867
1868         p = el;
1869
1870         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1871
1872         while (p) {
1873
1874             x += p.offsetLeft;
1875             y += p.offsetTop;
1876
1877             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1878                 hasAbsolute = true;
1879             }
1880
1881             if (Roo.isGecko) {
1882                 pe = fly(p);
1883
1884                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1885                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1886
1887
1888                 x += bl;
1889                 y += bt;
1890
1891
1892                 if (p != el && pe.getStyle('overflow') != 'visible') {
1893                     x += bl;
1894                     y += bt;
1895                 }
1896             }
1897             p = p.offsetParent;
1898         }
1899
1900         if (Roo.isSafari && hasAbsolute) {
1901             x -= bd.offsetLeft;
1902             y -= bd.offsetTop;
1903         }
1904
1905         if (Roo.isGecko && !hasAbsolute) {
1906             var dbd = fly(bd);
1907             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
1908             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
1909         }
1910
1911         p = el.parentNode;
1912         while (p && p != bd) {
1913             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
1914                 x -= p.scrollLeft;
1915                 y -= p.scrollTop;
1916             }
1917             p = p.parentNode;
1918         }
1919         return [x, y];
1920     },
1921  
1922   
1923
1924
1925     setXY : function(el, xy) {
1926         el = Roo.fly(el, '_setXY');
1927         el.position();
1928         var pts = el.translatePoints(xy);
1929         if (xy[0] !== false) {
1930             el.dom.style.left = pts.left + "px";
1931         }
1932         if (xy[1] !== false) {
1933             el.dom.style.top = pts.top + "px";
1934         }
1935     },
1936
1937     setX : function(el, x) {
1938         this.setXY(el, [x, false]);
1939     },
1940
1941     setY : function(el, y) {
1942         this.setXY(el, [false, y]);
1943     }
1944 };
1945 /*
1946  * Portions of this file are based on pieces of Yahoo User Interface Library
1947  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
1948  * YUI licensed under the BSD License:
1949  * http://developer.yahoo.net/yui/license.txt
1950  * <script type="text/javascript">
1951  *
1952  */
1953
1954 Roo.lib.Event = function() {
1955     var loadComplete = false;
1956     var listeners = [];
1957     var unloadListeners = [];
1958     var retryCount = 0;
1959     var onAvailStack = [];
1960     var counter = 0;
1961     var lastError = null;
1962
1963     return {
1964         POLL_RETRYS: 200,
1965         POLL_INTERVAL: 20,
1966         EL: 0,
1967         TYPE: 1,
1968         FN: 2,
1969         WFN: 3,
1970         OBJ: 3,
1971         ADJ_SCOPE: 4,
1972         _interval: null,
1973
1974         startInterval: function() {
1975             if (!this._interval) {
1976                 var self = this;
1977                 var callback = function() {
1978                     self._tryPreloadAttach();
1979                 };
1980                 this._interval = setInterval(callback, this.POLL_INTERVAL);
1981
1982             }
1983         },
1984
1985         onAvailable: function(p_id, p_fn, p_obj, p_override) {
1986             onAvailStack.push({ id:         p_id,
1987                 fn:         p_fn,
1988                 obj:        p_obj,
1989                 override:   p_override,
1990                 checkReady: false    });
1991
1992             retryCount = this.POLL_RETRYS;
1993             this.startInterval();
1994         },
1995
1996
1997         addListener: function(el, eventName, fn) {
1998             el = Roo.getDom(el);
1999             if (!el || !fn) {
2000                 return false;
2001             }
2002
2003             if ("unload" == eventName) {
2004                 unloadListeners[unloadListeners.length] =
2005                 [el, eventName, fn];
2006                 return true;
2007             }
2008
2009             var wrappedFn = function(e) {
2010                 return fn(Roo.lib.Event.getEvent(e));
2011             };
2012
2013             var li = [el, eventName, fn, wrappedFn];
2014
2015             var index = listeners.length;
2016             listeners[index] = li;
2017
2018             this.doAdd(el, eventName, wrappedFn, false);
2019             return true;
2020
2021         },
2022
2023
2024         removeListener: function(el, eventName, fn) {
2025             var i, len;
2026
2027             el = Roo.getDom(el);
2028
2029             if(!fn) {
2030                 return this.purgeElement(el, false, eventName);
2031             }
2032
2033
2034             if ("unload" == eventName) {
2035
2036                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2037                     var li = unloadListeners[i];
2038                     if (li &&
2039                         li[0] == el &&
2040                         li[1] == eventName &&
2041                         li[2] == fn) {
2042                         unloadListeners.splice(i, 1);
2043                         return true;
2044                     }
2045                 }
2046
2047                 return false;
2048             }
2049
2050             var cacheItem = null;
2051
2052
2053             var index = arguments[3];
2054
2055             if ("undefined" == typeof index) {
2056                 index = this._getCacheIndex(el, eventName, fn);
2057             }
2058
2059             if (index >= 0) {
2060                 cacheItem = listeners[index];
2061             }
2062
2063             if (!el || !cacheItem) {
2064                 return false;
2065             }
2066
2067             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2068
2069             delete listeners[index][this.WFN];
2070             delete listeners[index][this.FN];
2071             listeners.splice(index, 1);
2072
2073             return true;
2074
2075         },
2076
2077
2078         getTarget: function(ev, resolveTextNode) {
2079             ev = ev.browserEvent || ev;
2080             var t = ev.target || ev.srcElement;
2081             return this.resolveTextNode(t);
2082         },
2083
2084
2085         resolveTextNode: function(node) {
2086             if (Roo.isSafari && node && 3 == node.nodeType) {
2087                 return node.parentNode;
2088             } else {
2089                 return node;
2090             }
2091         },
2092
2093
2094         getPageX: function(ev) {
2095             ev = ev.browserEvent || ev;
2096             var x = ev.pageX;
2097             if (!x && 0 !== x) {
2098                 x = ev.clientX || 0;
2099
2100                 if (Roo.isIE) {
2101                     x += this.getScroll()[1];
2102                 }
2103             }
2104
2105             return x;
2106         },
2107
2108
2109         getPageY: function(ev) {
2110             ev = ev.browserEvent || ev;
2111             var y = ev.pageY;
2112             if (!y && 0 !== y) {
2113                 y = ev.clientY || 0;
2114
2115                 if (Roo.isIE) {
2116                     y += this.getScroll()[0];
2117                 }
2118             }
2119
2120
2121             return y;
2122         },
2123
2124
2125         getXY: function(ev) {
2126             ev = ev.browserEvent || ev;
2127             return [this.getPageX(ev), this.getPageY(ev)];
2128         },
2129
2130
2131         getRelatedTarget: function(ev) {
2132             ev = ev.browserEvent || ev;
2133             var t = ev.relatedTarget;
2134             if (!t) {
2135                 if (ev.type == "mouseout") {
2136                     t = ev.toElement;
2137                 } else if (ev.type == "mouseover") {
2138                     t = ev.fromElement;
2139                 }
2140             }
2141
2142             return this.resolveTextNode(t);
2143         },
2144
2145
2146         getTime: function(ev) {
2147             ev = ev.browserEvent || ev;
2148             if (!ev.time) {
2149                 var t = new Date().getTime();
2150                 try {
2151                     ev.time = t;
2152                 } catch(ex) {
2153                     this.lastError = ex;
2154                     return t;
2155                 }
2156             }
2157
2158             return ev.time;
2159         },
2160
2161
2162         stopEvent: function(ev) {
2163             this.stopPropagation(ev);
2164             this.preventDefault(ev);
2165         },
2166
2167
2168         stopPropagation: function(ev) {
2169             ev = ev.browserEvent || ev;
2170             if (ev.stopPropagation) {
2171                 ev.stopPropagation();
2172             } else {
2173                 ev.cancelBubble = true;
2174             }
2175         },
2176
2177
2178         preventDefault: function(ev) {
2179             ev = ev.browserEvent || ev;
2180             if(ev.preventDefault) {
2181                 ev.preventDefault();
2182             } else {
2183                 ev.returnValue = false;
2184             }
2185         },
2186
2187
2188         getEvent: function(e) {
2189             var ev = e || window.event;
2190             if (!ev) {
2191                 var c = this.getEvent.caller;
2192                 while (c) {
2193                     ev = c.arguments[0];
2194                     if (ev && Event == ev.constructor) {
2195                         break;
2196                     }
2197                     c = c.caller;
2198                 }
2199             }
2200             return ev;
2201         },
2202
2203
2204         getCharCode: function(ev) {
2205             ev = ev.browserEvent || ev;
2206             return ev.charCode || ev.keyCode || 0;
2207         },
2208
2209
2210         _getCacheIndex: function(el, eventName, fn) {
2211             for (var i = 0,len = listeners.length; i < len; ++i) {
2212                 var li = listeners[i];
2213                 if (li &&
2214                     li[this.FN] == fn &&
2215                     li[this.EL] == el &&
2216                     li[this.TYPE] == eventName) {
2217                     return i;
2218                 }
2219             }
2220
2221             return -1;
2222         },
2223
2224
2225         elCache: {},
2226
2227
2228         getEl: function(id) {
2229             return document.getElementById(id);
2230         },
2231
2232
2233         clearCache: function() {
2234         },
2235
2236
2237         _load: function(e) {
2238             loadComplete = true;
2239             var EU = Roo.lib.Event;
2240
2241
2242             if (Roo.isIE) {
2243                 EU.doRemove(window, "load", EU._load);
2244             }
2245         },
2246
2247
2248         _tryPreloadAttach: function() {
2249
2250             if (this.locked) {
2251                 return false;
2252             }
2253
2254             this.locked = true;
2255
2256
2257             var tryAgain = !loadComplete;
2258             if (!tryAgain) {
2259                 tryAgain = (retryCount > 0);
2260             }
2261
2262
2263             var notAvail = [];
2264             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2265                 var item = onAvailStack[i];
2266                 if (item) {
2267                     var el = this.getEl(item.id);
2268
2269                     if (el) {
2270                         if (!item.checkReady ||
2271                             loadComplete ||
2272                             el.nextSibling ||
2273                             (document && document.body)) {
2274
2275                             var scope = el;
2276                             if (item.override) {
2277                                 if (item.override === true) {
2278                                     scope = item.obj;
2279                                 } else {
2280                                     scope = item.override;
2281                                 }
2282                             }
2283                             item.fn.call(scope, item.obj);
2284                             onAvailStack[i] = null;
2285                         }
2286                     } else {
2287                         notAvail.push(item);
2288                     }
2289                 }
2290             }
2291
2292             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2293
2294             if (tryAgain) {
2295
2296                 this.startInterval();
2297             } else {
2298                 clearInterval(this._interval);
2299                 this._interval = null;
2300             }
2301
2302             this.locked = false;
2303
2304             return true;
2305
2306         },
2307
2308
2309         purgeElement: function(el, recurse, eventName) {
2310             var elListeners = this.getListeners(el, eventName);
2311             if (elListeners) {
2312                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2313                     var l = elListeners[i];
2314                     this.removeListener(el, l.type, l.fn);
2315                 }
2316             }
2317
2318             if (recurse && el && el.childNodes) {
2319                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2320                     this.purgeElement(el.childNodes[i], recurse, eventName);
2321                 }
2322             }
2323         },
2324
2325
2326         getListeners: function(el, eventName) {
2327             var results = [], searchLists;
2328             if (!eventName) {
2329                 searchLists = [listeners, unloadListeners];
2330             } else if (eventName == "unload") {
2331                 searchLists = [unloadListeners];
2332             } else {
2333                 searchLists = [listeners];
2334             }
2335
2336             for (var j = 0; j < searchLists.length; ++j) {
2337                 var searchList = searchLists[j];
2338                 if (searchList && searchList.length > 0) {
2339                     for (var i = 0,len = searchList.length; i < len; ++i) {
2340                         var l = searchList[i];
2341                         if (l && l[this.EL] === el &&
2342                             (!eventName || eventName === l[this.TYPE])) {
2343                             results.push({
2344                                 type:   l[this.TYPE],
2345                                 fn:     l[this.FN],
2346                                 obj:    l[this.OBJ],
2347                                 adjust: l[this.ADJ_SCOPE],
2348                                 index:  i
2349                             });
2350                         }
2351                     }
2352                 }
2353             }
2354
2355             return (results.length) ? results : null;
2356         },
2357
2358
2359         _unload: function(e) {
2360
2361             var EU = Roo.lib.Event, i, j, l, len, index;
2362
2363             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2364                 l = unloadListeners[i];
2365                 if (l) {
2366                     var scope = window;
2367                     if (l[EU.ADJ_SCOPE]) {
2368                         if (l[EU.ADJ_SCOPE] === true) {
2369                             scope = l[EU.OBJ];
2370                         } else {
2371                             scope = l[EU.ADJ_SCOPE];
2372                         }
2373                     }
2374                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2375                     unloadListeners[i] = null;
2376                     l = null;
2377                     scope = null;
2378                 }
2379             }
2380
2381             unloadListeners = null;
2382
2383             if (listeners && listeners.length > 0) {
2384                 j = listeners.length;
2385                 while (j) {
2386                     index = j - 1;
2387                     l = listeners[index];
2388                     if (l) {
2389                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2390                                 l[EU.FN], index);
2391                     }
2392                     j = j - 1;
2393                 }
2394                 l = null;
2395
2396                 EU.clearCache();
2397             }
2398
2399             EU.doRemove(window, "unload", EU._unload);
2400
2401         },
2402
2403
2404         getScroll: function() {
2405             var dd = document.documentElement, db = document.body;
2406             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2407                 return [dd.scrollTop, dd.scrollLeft];
2408             } else if (db) {
2409                 return [db.scrollTop, db.scrollLeft];
2410             } else {
2411                 return [0, 0];
2412             }
2413         },
2414
2415
2416         doAdd: function () {
2417             if (window.addEventListener) {
2418                 return function(el, eventName, fn, capture) {
2419                     el.addEventListener(eventName, fn, (capture));
2420                 };
2421             } else if (window.attachEvent) {
2422                 return function(el, eventName, fn, capture) {
2423                     el.attachEvent("on" + eventName, fn);
2424                 };
2425             } else {
2426                 return function() {
2427                 };
2428             }
2429         }(),
2430
2431
2432         doRemove: function() {
2433             if (window.removeEventListener) {
2434                 return function (el, eventName, fn, capture) {
2435                     el.removeEventListener(eventName, fn, (capture));
2436                 };
2437             } else if (window.detachEvent) {
2438                 return function (el, eventName, fn) {
2439                     el.detachEvent("on" + eventName, fn);
2440                 };
2441             } else {
2442                 return function() {
2443                 };
2444             }
2445         }()
2446     };
2447     
2448 }();
2449 (function() {     
2450    
2451     var E = Roo.lib.Event;
2452     E.on = E.addListener;
2453     E.un = E.removeListener;
2454
2455     if (document && document.body) {
2456         E._load();
2457     } else {
2458         E.doAdd(window, "load", E._load);
2459     }
2460     E.doAdd(window, "unload", E._unload);
2461     E._tryPreloadAttach();
2462 })();
2463
2464 /*
2465  * Portions of this file are based on pieces of Yahoo User Interface Library
2466  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2467  * YUI licensed under the BSD License:
2468  * http://developer.yahoo.net/yui/license.txt
2469  * <script type="text/javascript">
2470  *
2471  */
2472
2473 (function() {
2474     /**
2475      * @class Roo.lib.Ajax
2476      *
2477      */
2478     Roo.lib.Ajax = {
2479         /**
2480          * @static 
2481          */
2482         request : function(method, uri, cb, data, options) {
2483             if(options){
2484                 var hs = options.headers;
2485                 if(hs){
2486                     for(var h in hs){
2487                         if(hs.hasOwnProperty(h)){
2488                             this.initHeader(h, hs[h], false);
2489                         }
2490                     }
2491                 }
2492                 if(options.xmlData){
2493                     this.initHeader('Content-Type', 'text/xml', false);
2494                     method = 'POST';
2495                     data = options.xmlData;
2496                 }
2497             }
2498
2499             return this.asyncRequest(method, uri, cb, data);
2500         },
2501
2502         serializeForm : function(form) {
2503             if(typeof form == 'string') {
2504                 form = (document.getElementById(form) || document.forms[form]);
2505             }
2506
2507             var el, name, val, disabled, data = '', hasSubmit = false;
2508             for (var i = 0; i < form.elements.length; i++) {
2509                 el = form.elements[i];
2510                 disabled = form.elements[i].disabled;
2511                 name = form.elements[i].name;
2512                 val = form.elements[i].value;
2513
2514                 if (!disabled && name){
2515                     switch (el.type)
2516                             {
2517                         case 'select-one':
2518                         case 'select-multiple':
2519                             for (var j = 0; j < el.options.length; j++) {
2520                                 if (el.options[j].selected) {
2521                                     if (Roo.isIE) {
2522                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2523                                     }
2524                                     else {
2525                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2526                                     }
2527                                 }
2528                             }
2529                             break;
2530                         case 'radio':
2531                         case 'checkbox':
2532                             if (el.checked) {
2533                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2534                             }
2535                             break;
2536                         case 'file':
2537
2538                         case undefined:
2539
2540                         case 'reset':
2541
2542                         case 'button':
2543
2544                             break;
2545                         case 'submit':
2546                             if(hasSubmit == false) {
2547                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2548                                 hasSubmit = true;
2549                             }
2550                             break;
2551                         default:
2552                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2553                             break;
2554                     }
2555                 }
2556             }
2557             data = data.substr(0, data.length - 1);
2558             return data;
2559         },
2560
2561         headers:{},
2562
2563         hasHeaders:false,
2564
2565         useDefaultHeader:true,
2566
2567         defaultPostHeader:'application/x-www-form-urlencoded',
2568
2569         useDefaultXhrHeader:true,
2570
2571         defaultXhrHeader:'XMLHttpRequest',
2572
2573         hasDefaultHeaders:true,
2574
2575         defaultHeaders:{},
2576
2577         poll:{},
2578
2579         timeout:{},
2580
2581         pollInterval:50,
2582
2583         transactionId:0,
2584
2585         setProgId:function(id)
2586         {
2587             this.activeX.unshift(id);
2588         },
2589
2590         setDefaultPostHeader:function(b)
2591         {
2592             this.useDefaultHeader = b;
2593         },
2594
2595         setDefaultXhrHeader:function(b)
2596         {
2597             this.useDefaultXhrHeader = b;
2598         },
2599
2600         setPollingInterval:function(i)
2601         {
2602             if (typeof i == 'number' && isFinite(i)) {
2603                 this.pollInterval = i;
2604             }
2605         },
2606
2607         createXhrObject:function(transactionId)
2608         {
2609             var obj,http;
2610             try
2611             {
2612
2613                 http = new XMLHttpRequest();
2614
2615                 obj = { conn:http, tId:transactionId };
2616             }
2617             catch(e)
2618             {
2619                 for (var i = 0; i < this.activeX.length; ++i) {
2620                     try
2621                     {
2622
2623                         http = new ActiveXObject(this.activeX[i]);
2624
2625                         obj = { conn:http, tId:transactionId };
2626                         break;
2627                     }
2628                     catch(e) {
2629                     }
2630                 }
2631             }
2632             finally
2633             {
2634                 return obj;
2635             }
2636         },
2637
2638         getConnectionObject:function()
2639         {
2640             var o;
2641             var tId = this.transactionId;
2642
2643             try
2644             {
2645                 o = this.createXhrObject(tId);
2646                 if (o) {
2647                     this.transactionId++;
2648                 }
2649             }
2650             catch(e) {
2651             }
2652             finally
2653             {
2654                 return o;
2655             }
2656         },
2657
2658         asyncRequest:function(method, uri, callback, postData)
2659         {
2660             var o = this.getConnectionObject();
2661
2662             if (!o) {
2663                 return null;
2664             }
2665             else {
2666                 o.conn.open(method, uri, true);
2667
2668                 if (this.useDefaultXhrHeader) {
2669                     if (!this.defaultHeaders['X-Requested-With']) {
2670                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2671                     }
2672                 }
2673
2674                 if(postData && this.useDefaultHeader){
2675                     this.initHeader('Content-Type', this.defaultPostHeader);
2676                 }
2677
2678                  if (this.hasDefaultHeaders || this.hasHeaders) {
2679                     this.setHeader(o);
2680                 }
2681
2682                 this.handleReadyState(o, callback);
2683                 o.conn.send(postData || null);
2684
2685                 return o;
2686             }
2687         },
2688
2689         handleReadyState:function(o, callback)
2690         {
2691             var oConn = this;
2692
2693             if (callback && callback.timeout) {
2694                 this.timeout[o.tId] = window.setTimeout(function() {
2695                     oConn.abort(o, callback, true);
2696                 }, callback.timeout);
2697             }
2698
2699             this.poll[o.tId] = window.setInterval(
2700                     function() {
2701                         if (o.conn && o.conn.readyState == 4) {
2702                             window.clearInterval(oConn.poll[o.tId]);
2703                             delete oConn.poll[o.tId];
2704
2705                             if(callback && callback.timeout) {
2706                                 window.clearTimeout(oConn.timeout[o.tId]);
2707                                 delete oConn.timeout[o.tId];
2708                             }
2709
2710                             oConn.handleTransactionResponse(o, callback);
2711                         }
2712                     }
2713                     , this.pollInterval);
2714         },
2715
2716         handleTransactionResponse:function(o, callback, isAbort)
2717         {
2718
2719             if (!callback) {
2720                 this.releaseObject(o);
2721                 return;
2722             }
2723
2724             var httpStatus, responseObject;
2725
2726             try
2727             {
2728                 if (o.conn.status !== undefined && o.conn.status != 0) {
2729                     httpStatus = o.conn.status;
2730                 }
2731                 else {
2732                     httpStatus = 13030;
2733                 }
2734             }
2735             catch(e) {
2736
2737
2738                 httpStatus = 13030;
2739             }
2740
2741             if (httpStatus >= 200 && httpStatus < 300) {
2742                 responseObject = this.createResponseObject(o, callback.argument);
2743                 if (callback.success) {
2744                     if (!callback.scope) {
2745                         callback.success(responseObject);
2746                     }
2747                     else {
2748
2749
2750                         callback.success.apply(callback.scope, [responseObject]);
2751                     }
2752                 }
2753             }
2754             else {
2755                 switch (httpStatus) {
2756
2757                     case 12002:
2758                     case 12029:
2759                     case 12030:
2760                     case 12031:
2761                     case 12152:
2762                     case 13030:
2763                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2764                         if (callback.failure) {
2765                             if (!callback.scope) {
2766                                 callback.failure(responseObject);
2767                             }
2768                             else {
2769                                 callback.failure.apply(callback.scope, [responseObject]);
2770                             }
2771                         }
2772                         break;
2773                     default:
2774                         responseObject = this.createResponseObject(o, callback.argument);
2775                         if (callback.failure) {
2776                             if (!callback.scope) {
2777                                 callback.failure(responseObject);
2778                             }
2779                             else {
2780                                 callback.failure.apply(callback.scope, [responseObject]);
2781                             }
2782                         }
2783                 }
2784             }
2785
2786             this.releaseObject(o);
2787             responseObject = null;
2788         },
2789
2790         createResponseObject:function(o, callbackArg)
2791         {
2792             var obj = {};
2793             var headerObj = {};
2794
2795             try
2796             {
2797                 var headerStr = o.conn.getAllResponseHeaders();
2798                 var header = headerStr.split('\n');
2799                 for (var i = 0; i < header.length; i++) {
2800                     var delimitPos = header[i].indexOf(':');
2801                     if (delimitPos != -1) {
2802                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2803                     }
2804                 }
2805             }
2806             catch(e) {
2807             }
2808
2809             obj.tId = o.tId;
2810             obj.status = o.conn.status;
2811             obj.statusText = o.conn.statusText;
2812             obj.getResponseHeader = headerObj;
2813             obj.getAllResponseHeaders = headerStr;
2814             obj.responseText = o.conn.responseText;
2815             obj.responseXML = o.conn.responseXML;
2816
2817             if (typeof callbackArg !== undefined) {
2818                 obj.argument = callbackArg;
2819             }
2820
2821             return obj;
2822         },
2823
2824         createExceptionObject:function(tId, callbackArg, isAbort)
2825         {
2826             var COMM_CODE = 0;
2827             var COMM_ERROR = 'communication failure';
2828             var ABORT_CODE = -1;
2829             var ABORT_ERROR = 'transaction aborted';
2830
2831             var obj = {};
2832
2833             obj.tId = tId;
2834             if (isAbort) {
2835                 obj.status = ABORT_CODE;
2836                 obj.statusText = ABORT_ERROR;
2837             }
2838             else {
2839                 obj.status = COMM_CODE;
2840                 obj.statusText = COMM_ERROR;
2841             }
2842
2843             if (callbackArg) {
2844                 obj.argument = callbackArg;
2845             }
2846
2847             return obj;
2848         },
2849
2850         initHeader:function(label, value, isDefault)
2851         {
2852             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2853
2854             if (headerObj[label] === undefined) {
2855                 headerObj[label] = value;
2856             }
2857             else {
2858
2859
2860                 headerObj[label] = value + "," + headerObj[label];
2861             }
2862
2863             if (isDefault) {
2864                 this.hasDefaultHeaders = true;
2865             }
2866             else {
2867                 this.hasHeaders = true;
2868             }
2869         },
2870
2871
2872         setHeader:function(o)
2873         {
2874             if (this.hasDefaultHeaders) {
2875                 for (var prop in this.defaultHeaders) {
2876                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2877                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2878                     }
2879                 }
2880             }
2881
2882             if (this.hasHeaders) {
2883                 for (var prop in this.headers) {
2884                     if (this.headers.hasOwnProperty(prop)) {
2885                         o.conn.setRequestHeader(prop, this.headers[prop]);
2886                     }
2887                 }
2888                 this.headers = {};
2889                 this.hasHeaders = false;
2890             }
2891         },
2892
2893         resetDefaultHeaders:function() {
2894             delete this.defaultHeaders;
2895             this.defaultHeaders = {};
2896             this.hasDefaultHeaders = false;
2897         },
2898
2899         abort:function(o, callback, isTimeout)
2900         {
2901             if(this.isCallInProgress(o)) {
2902                 o.conn.abort();
2903                 window.clearInterval(this.poll[o.tId]);
2904                 delete this.poll[o.tId];
2905                 if (isTimeout) {
2906                     delete this.timeout[o.tId];
2907                 }
2908
2909                 this.handleTransactionResponse(o, callback, true);
2910
2911                 return true;
2912             }
2913             else {
2914                 return false;
2915             }
2916         },
2917
2918
2919         isCallInProgress:function(o)
2920         {
2921             if (o && o.conn) {
2922                 return o.conn.readyState != 4 && o.conn.readyState != 0;
2923             }
2924             else {
2925
2926                 return false;
2927             }
2928         },
2929
2930
2931         releaseObject:function(o)
2932         {
2933
2934             o.conn = null;
2935
2936             o = null;
2937         },
2938
2939         activeX:[
2940         'MSXML2.XMLHTTP.3.0',
2941         'MSXML2.XMLHTTP',
2942         'Microsoft.XMLHTTP'
2943         ]
2944
2945
2946     };
2947 })();/*
2948  * Portions of this file are based on pieces of Yahoo User Interface Library
2949  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2950  * YUI licensed under the BSD License:
2951  * http://developer.yahoo.net/yui/license.txt
2952  * <script type="text/javascript">
2953  *
2954  */
2955
2956 Roo.lib.Region = function(t, r, b, l) {
2957     this.top = t;
2958     this[1] = t;
2959     this.right = r;
2960     this.bottom = b;
2961     this.left = l;
2962     this[0] = l;
2963 };
2964
2965
2966 Roo.lib.Region.prototype = {
2967     contains : function(region) {
2968         return ( region.left >= this.left &&
2969                  region.right <= this.right &&
2970                  region.top >= this.top &&
2971                  region.bottom <= this.bottom    );
2972
2973     },
2974
2975     getArea : function() {
2976         return ( (this.bottom - this.top) * (this.right - this.left) );
2977     },
2978
2979     intersect : function(region) {
2980         var t = Math.max(this.top, region.top);
2981         var r = Math.min(this.right, region.right);
2982         var b = Math.min(this.bottom, region.bottom);
2983         var l = Math.max(this.left, region.left);
2984
2985         if (b >= t && r >= l) {
2986             return new Roo.lib.Region(t, r, b, l);
2987         } else {
2988             return null;
2989         }
2990     },
2991     union : function(region) {
2992         var t = Math.min(this.top, region.top);
2993         var r = Math.max(this.right, region.right);
2994         var b = Math.max(this.bottom, region.bottom);
2995         var l = Math.min(this.left, region.left);
2996
2997         return new Roo.lib.Region(t, r, b, l);
2998     },
2999
3000     adjust : function(t, l, b, r) {
3001         this.top += t;
3002         this.left += l;
3003         this.right += r;
3004         this.bottom += b;
3005         return this;
3006     }
3007 };
3008
3009 Roo.lib.Region.getRegion = function(el) {
3010     var p = Roo.lib.Dom.getXY(el);
3011
3012     var t = p[1];
3013     var r = p[0] + el.offsetWidth;
3014     var b = p[1] + el.offsetHeight;
3015     var l = p[0];
3016
3017     return new Roo.lib.Region(t, r, b, l);
3018 };
3019 /*
3020  * Portions of this file are based on pieces of Yahoo User Interface Library
3021  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3022  * YUI licensed under the BSD License:
3023  * http://developer.yahoo.net/yui/license.txt
3024  * <script type="text/javascript">
3025  *
3026  */
3027 //@@dep Roo.lib.Region
3028
3029
3030 Roo.lib.Point = function(x, y) {
3031     if (x instanceof Array) {
3032         y = x[1];
3033         x = x[0];
3034     }
3035     this.x = this.right = this.left = this[0] = x;
3036     this.y = this.top = this.bottom = this[1] = y;
3037 };
3038
3039 Roo.lib.Point.prototype = new Roo.lib.Region();
3040 /*
3041  * Portions of this file are based on pieces of Yahoo User Interface Library
3042  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3043  * YUI licensed under the BSD License:
3044  * http://developer.yahoo.net/yui/license.txt
3045  * <script type="text/javascript">
3046  *
3047  */
3048  
3049 (function() {   
3050
3051     Roo.lib.Anim = {
3052         scroll : function(el, args, duration, easing, cb, scope) {
3053             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3054         },
3055
3056         motion : function(el, args, duration, easing, cb, scope) {
3057             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3058         },
3059
3060         color : function(el, args, duration, easing, cb, scope) {
3061             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3062         },
3063
3064         run : function(el, args, duration, easing, cb, scope, type) {
3065             type = type || Roo.lib.AnimBase;
3066             if (typeof easing == "string") {
3067                 easing = Roo.lib.Easing[easing];
3068             }
3069             var anim = new type(el, args, duration, easing);
3070             anim.animateX(function() {
3071                 Roo.callback(cb, scope);
3072             });
3073             return anim;
3074         }
3075     };
3076 })();/*
3077  * Portions of this file are based on pieces of Yahoo User Interface Library
3078  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3079  * YUI licensed under the BSD License:
3080  * http://developer.yahoo.net/yui/license.txt
3081  * <script type="text/javascript">
3082  *
3083  */
3084
3085 (function() {    
3086     var libFlyweight;
3087     
3088     function fly(el) {
3089         if (!libFlyweight) {
3090             libFlyweight = new Roo.Element.Flyweight();
3091         }
3092         libFlyweight.dom = el;
3093         return libFlyweight;
3094     }
3095
3096     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3097     
3098    
3099     
3100     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3101         if (el) {
3102             this.init(el, attributes, duration, method);
3103         }
3104     };
3105
3106     Roo.lib.AnimBase.fly = fly;
3107     
3108     
3109     
3110     Roo.lib.AnimBase.prototype = {
3111
3112         toString: function() {
3113             var el = this.getEl();
3114             var id = el.id || el.tagName;
3115             return ("Anim " + id);
3116         },
3117
3118         patterns: {
3119             noNegatives:        /width|height|opacity|padding/i,
3120             offsetAttribute:  /^((width|height)|(top|left))$/,
3121             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3122             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3123         },
3124
3125
3126         doMethod: function(attr, start, end) {
3127             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3128         },
3129
3130
3131         setAttribute: function(attr, val, unit) {
3132             if (this.patterns.noNegatives.test(attr)) {
3133                 val = (val > 0) ? val : 0;
3134             }
3135
3136             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3137         },
3138
3139
3140         getAttribute: function(attr) {
3141             var el = this.getEl();
3142             var val = fly(el).getStyle(attr);
3143
3144             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3145                 return parseFloat(val);
3146             }
3147
3148             var a = this.patterns.offsetAttribute.exec(attr) || [];
3149             var pos = !!( a[3] );
3150             var box = !!( a[2] );
3151
3152
3153             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3154                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3155             } else {
3156                 val = 0;
3157             }
3158
3159             return val;
3160         },
3161
3162
3163         getDefaultUnit: function(attr) {
3164             if (this.patterns.defaultUnit.test(attr)) {
3165                 return 'px';
3166             }
3167
3168             return '';
3169         },
3170
3171         animateX : function(callback, scope) {
3172             var f = function() {
3173                 this.onComplete.removeListener(f);
3174                 if (typeof callback == "function") {
3175                     callback.call(scope || this, this);
3176                 }
3177             };
3178             this.onComplete.addListener(f, this);
3179             this.animate();
3180         },
3181
3182
3183         setRuntimeAttribute: function(attr) {
3184             var start;
3185             var end;
3186             var attributes = this.attributes;
3187
3188             this.runtimeAttributes[attr] = {};
3189
3190             var isset = function(prop) {
3191                 return (typeof prop !== 'undefined');
3192             };
3193
3194             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3195                 return false;
3196             }
3197
3198             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3199
3200
3201             if (isset(attributes[attr]['to'])) {
3202                 end = attributes[attr]['to'];
3203             } else if (isset(attributes[attr]['by'])) {
3204                 if (start.constructor == Array) {
3205                     end = [];
3206                     for (var i = 0, len = start.length; i < len; ++i) {
3207                         end[i] = start[i] + attributes[attr]['by'][i];
3208                     }
3209                 } else {
3210                     end = start + attributes[attr]['by'];
3211                 }
3212             }
3213
3214             this.runtimeAttributes[attr].start = start;
3215             this.runtimeAttributes[attr].end = end;
3216
3217
3218             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3219         },
3220
3221
3222         init: function(el, attributes, duration, method) {
3223
3224             var isAnimated = false;
3225
3226
3227             var startTime = null;
3228
3229
3230             var actualFrames = 0;
3231
3232
3233             el = Roo.getDom(el);
3234
3235
3236             this.attributes = attributes || {};
3237
3238
3239             this.duration = duration || 1;
3240
3241
3242             this.method = method || Roo.lib.Easing.easeNone;
3243
3244
3245             this.useSeconds = true;
3246
3247
3248             this.currentFrame = 0;
3249
3250
3251             this.totalFrames = Roo.lib.AnimMgr.fps;
3252
3253
3254             this.getEl = function() {
3255                 return el;
3256             };
3257
3258
3259             this.isAnimated = function() {
3260                 return isAnimated;
3261             };
3262
3263
3264             this.getStartTime = function() {
3265                 return startTime;
3266             };
3267
3268             this.runtimeAttributes = {};
3269
3270
3271             this.animate = function() {
3272                 if (this.isAnimated()) {
3273                     return false;
3274                 }
3275
3276                 this.currentFrame = 0;
3277
3278                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3279
3280                 Roo.lib.AnimMgr.registerElement(this);
3281             };
3282
3283
3284             this.stop = function(finish) {
3285                 if (finish) {
3286                     this.currentFrame = this.totalFrames;
3287                     this._onTween.fire();
3288                 }
3289                 Roo.lib.AnimMgr.stop(this);
3290             };
3291
3292             var onStart = function() {
3293                 this.onStart.fire();
3294
3295                 this.runtimeAttributes = {};
3296                 for (var attr in this.attributes) {
3297                     this.setRuntimeAttribute(attr);
3298                 }
3299
3300                 isAnimated = true;
3301                 actualFrames = 0;
3302                 startTime = new Date();
3303             };
3304
3305
3306             var onTween = function() {
3307                 var data = {
3308                     duration: new Date() - this.getStartTime(),
3309                     currentFrame: this.currentFrame
3310                 };
3311
3312                 data.toString = function() {
3313                     return (
3314                             'duration: ' + data.duration +
3315                             ', currentFrame: ' + data.currentFrame
3316                             );
3317                 };
3318
3319                 this.onTween.fire(data);
3320
3321                 var runtimeAttributes = this.runtimeAttributes;
3322
3323                 for (var attr in runtimeAttributes) {
3324                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3325                 }
3326
3327                 actualFrames += 1;
3328             };
3329
3330             var onComplete = function() {
3331                 var actual_duration = (new Date() - startTime) / 1000 ;
3332
3333                 var data = {
3334                     duration: actual_duration,
3335                     frames: actualFrames,
3336                     fps: actualFrames / actual_duration
3337                 };
3338
3339                 data.toString = function() {
3340                     return (
3341                             'duration: ' + data.duration +
3342                             ', frames: ' + data.frames +
3343                             ', fps: ' + data.fps
3344                             );
3345                 };
3346
3347                 isAnimated = false;
3348                 actualFrames = 0;
3349                 this.onComplete.fire(data);
3350             };
3351
3352
3353             this._onStart = new Roo.util.Event(this);
3354             this.onStart = new Roo.util.Event(this);
3355             this.onTween = new Roo.util.Event(this);
3356             this._onTween = new Roo.util.Event(this);
3357             this.onComplete = new Roo.util.Event(this);
3358             this._onComplete = new Roo.util.Event(this);
3359             this._onStart.addListener(onStart);
3360             this._onTween.addListener(onTween);
3361             this._onComplete.addListener(onComplete);
3362         }
3363     };
3364 })();
3365 /*
3366  * Portions of this file are based on pieces of Yahoo User Interface Library
3367  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3368  * YUI licensed under the BSD License:
3369  * http://developer.yahoo.net/yui/license.txt
3370  * <script type="text/javascript">
3371  *
3372  */
3373
3374 Roo.lib.AnimMgr = new function() {
3375
3376         var thread = null;
3377
3378
3379         var queue = [];
3380
3381
3382         var tweenCount = 0;
3383
3384
3385         this.fps = 1000;
3386
3387
3388         this.delay = 1;
3389
3390
3391         this.registerElement = function(tween) {
3392             queue[queue.length] = tween;
3393             tweenCount += 1;
3394             tween._onStart.fire();
3395             this.start();
3396         };
3397
3398
3399         this.unRegister = function(tween, index) {
3400             tween._onComplete.fire();
3401             index = index || getIndex(tween);
3402             if (index != -1) {
3403                 queue.splice(index, 1);
3404             }
3405
3406             tweenCount -= 1;
3407             if (tweenCount <= 0) {
3408                 this.stop();
3409             }
3410         };
3411
3412
3413         this.start = function() {
3414             if (thread === null) {
3415                 thread = setInterval(this.run, this.delay);
3416             }
3417         };
3418
3419
3420         this.stop = function(tween) {
3421             if (!tween) {
3422                 clearInterval(thread);
3423
3424                 for (var i = 0, len = queue.length; i < len; ++i) {
3425                     if (queue[0].isAnimated()) {
3426                         this.unRegister(queue[0], 0);
3427                     }
3428                 }
3429
3430                 queue = [];
3431                 thread = null;
3432                 tweenCount = 0;
3433             }
3434             else {
3435                 this.unRegister(tween);
3436             }
3437         };
3438
3439
3440         this.run = function() {
3441             for (var i = 0, len = queue.length; i < len; ++i) {
3442                 var tween = queue[i];
3443                 if (!tween || !tween.isAnimated()) {
3444                     continue;
3445                 }
3446
3447                 if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3448                 {
3449                     tween.currentFrame += 1;
3450
3451                     if (tween.useSeconds) {
3452                         correctFrame(tween);
3453                     }
3454                     tween._onTween.fire();
3455                 }
3456                 else {
3457                     Roo.lib.AnimMgr.stop(tween, i);
3458                 }
3459             }
3460         };
3461
3462         var getIndex = function(anim) {
3463             for (var i = 0, len = queue.length; i < len; ++i) {
3464                 if (queue[i] == anim) {
3465                     return i;
3466                 }
3467             }
3468             return -1;
3469         };
3470
3471
3472         var correctFrame = function(tween) {
3473             var frames = tween.totalFrames;
3474             var frame = tween.currentFrame;
3475             var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3476             var elapsed = (new Date() - tween.getStartTime());
3477             var tweak = 0;
3478
3479             if (elapsed < tween.duration * 1000) {
3480                 tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3481             } else {
3482                 tweak = frames - (frame + 1);
3483             }
3484             if (tweak > 0 && isFinite(tweak)) {
3485                 if (tween.currentFrame + tweak >= frames) {
3486                     tweak = frames - (frame + 1);
3487                 }
3488
3489                 tween.currentFrame += tweak;
3490             }
3491         };
3492     };/*
3493  * Portions of this file are based on pieces of Yahoo User Interface Library
3494  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3495  * YUI licensed under the BSD License:
3496  * http://developer.yahoo.net/yui/license.txt
3497  * <script type="text/javascript">
3498  *
3499  */
3500 Roo.lib.Bezier = new function() {
3501
3502         this.getPosition = function(points, t) {
3503             var n = points.length;
3504             var tmp = [];
3505
3506             for (var i = 0; i < n; ++i) {
3507                 tmp[i] = [points[i][0], points[i][1]];
3508             }
3509
3510             for (var j = 1; j < n; ++j) {
3511                 for (i = 0; i < n - j; ++i) {
3512                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3513                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3514                 }
3515             }
3516
3517             return [ tmp[0][0], tmp[0][1] ];
3518
3519         };
3520     };/*
3521  * Portions of this file are based on pieces of Yahoo User Interface Library
3522  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3523  * YUI licensed under the BSD License:
3524  * http://developer.yahoo.net/yui/license.txt
3525  * <script type="text/javascript">
3526  *
3527  */
3528 (function() {
3529
3530     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3531         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3532     };
3533
3534     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3535
3536     var fly = Roo.lib.AnimBase.fly;
3537     var Y = Roo.lib;
3538     var superclass = Y.ColorAnim.superclass;
3539     var proto = Y.ColorAnim.prototype;
3540
3541     proto.toString = function() {
3542         var el = this.getEl();
3543         var id = el.id || el.tagName;
3544         return ("ColorAnim " + id);
3545     };
3546
3547     proto.patterns.color = /color$/i;
3548     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3549     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3550     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3551     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3552
3553
3554     proto.parseColor = function(s) {
3555         if (s.length == 3) {
3556             return s;
3557         }
3558
3559         var c = this.patterns.hex.exec(s);
3560         if (c && c.length == 4) {
3561             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3562         }
3563
3564         c = this.patterns.rgb.exec(s);
3565         if (c && c.length == 4) {
3566             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3567         }
3568
3569         c = this.patterns.hex3.exec(s);
3570         if (c && c.length == 4) {
3571             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3572         }
3573
3574         return null;
3575     };
3576     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3577     proto.getAttribute = function(attr) {
3578         var el = this.getEl();
3579         if (this.patterns.color.test(attr)) {
3580             var val = fly(el).getStyle(attr);
3581
3582             if (this.patterns.transparent.test(val)) {
3583                 var parent = el.parentNode;
3584                 val = fly(parent).getStyle(attr);
3585
3586                 while (parent && this.patterns.transparent.test(val)) {
3587                     parent = parent.parentNode;
3588                     val = fly(parent).getStyle(attr);
3589                     if (parent.tagName.toUpperCase() == 'HTML') {
3590                         val = '#fff';
3591                     }
3592                 }
3593             }
3594         } else {
3595             val = superclass.getAttribute.call(this, attr);
3596         }
3597
3598         return val;
3599     };
3600     proto.getAttribute = function(attr) {
3601         var el = this.getEl();
3602         if (this.patterns.color.test(attr)) {
3603             var val = fly(el).getStyle(attr);
3604
3605             if (this.patterns.transparent.test(val)) {
3606                 var parent = el.parentNode;
3607                 val = fly(parent).getStyle(attr);
3608
3609                 while (parent && this.patterns.transparent.test(val)) {
3610                     parent = parent.parentNode;
3611                     val = fly(parent).getStyle(attr);
3612                     if (parent.tagName.toUpperCase() == 'HTML') {
3613                         val = '#fff';
3614                     }
3615                 }
3616             }
3617         } else {
3618             val = superclass.getAttribute.call(this, attr);
3619         }
3620
3621         return val;
3622     };
3623
3624     proto.doMethod = function(attr, start, end) {
3625         var val;
3626
3627         if (this.patterns.color.test(attr)) {
3628             val = [];
3629             for (var i = 0, len = start.length; i < len; ++i) {
3630                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3631             }
3632
3633             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3634         }
3635         else {
3636             val = superclass.doMethod.call(this, attr, start, end);
3637         }
3638
3639         return val;
3640     };
3641
3642     proto.setRuntimeAttribute = function(attr) {
3643         superclass.setRuntimeAttribute.call(this, attr);
3644
3645         if (this.patterns.color.test(attr)) {
3646             var attributes = this.attributes;
3647             var start = this.parseColor(this.runtimeAttributes[attr].start);
3648             var end = this.parseColor(this.runtimeAttributes[attr].end);
3649
3650             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3651                 end = this.parseColor(attributes[attr].by);
3652
3653                 for (var i = 0, len = start.length; i < len; ++i) {
3654                     end[i] = start[i] + end[i];
3655                 }
3656             }
3657
3658             this.runtimeAttributes[attr].start = start;
3659             this.runtimeAttributes[attr].end = end;
3660         }
3661     };
3662 })();
3663
3664 /*
3665  * Portions of this file are based on pieces of Yahoo User Interface Library
3666  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3667  * YUI licensed under the BSD License:
3668  * http://developer.yahoo.net/yui/license.txt
3669  * <script type="text/javascript">
3670  *
3671  */
3672 Roo.lib.Easing = {
3673
3674
3675     easeNone: function (t, b, c, d) {
3676         return c * t / d + b;
3677     },
3678
3679
3680     easeIn: function (t, b, c, d) {
3681         return c * (t /= d) * t + b;
3682     },
3683
3684
3685     easeOut: function (t, b, c, d) {
3686         return -c * (t /= d) * (t - 2) + b;
3687     },
3688
3689
3690     easeBoth: function (t, b, c, d) {
3691         if ((t /= d / 2) < 1) {
3692             return c / 2 * t * t + b;
3693         }
3694
3695         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3696     },
3697
3698
3699     easeInStrong: function (t, b, c, d) {
3700         return c * (t /= d) * t * t * t + b;
3701     },
3702
3703
3704     easeOutStrong: function (t, b, c, d) {
3705         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3706     },
3707
3708
3709     easeBothStrong: function (t, b, c, d) {
3710         if ((t /= d / 2) < 1) {
3711             return c / 2 * t * t * t * t + b;
3712         }
3713
3714         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3715     },
3716
3717
3718
3719     elasticIn: function (t, b, c, d, a, p) {
3720         if (t == 0) {
3721             return b;
3722         }
3723         if ((t /= d) == 1) {
3724             return b + c;
3725         }
3726         if (!p) {
3727             p = d * .3;
3728         }
3729
3730         if (!a || a < Math.abs(c)) {
3731             a = c;
3732             var s = p / 4;
3733         }
3734         else {
3735             var s = p / (2 * Math.PI) * Math.asin(c / a);
3736         }
3737
3738         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3739     },
3740
3741
3742     elasticOut: function (t, b, c, d, a, p) {
3743         if (t == 0) {
3744             return b;
3745         }
3746         if ((t /= d) == 1) {
3747             return b + c;
3748         }
3749         if (!p) {
3750             p = d * .3;
3751         }
3752
3753         if (!a || a < Math.abs(c)) {
3754             a = c;
3755             var s = p / 4;
3756         }
3757         else {
3758             var s = p / (2 * Math.PI) * Math.asin(c / a);
3759         }
3760
3761         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3762     },
3763
3764
3765     elasticBoth: function (t, b, c, d, a, p) {
3766         if (t == 0) {
3767             return b;
3768         }
3769
3770         if ((t /= d / 2) == 2) {
3771             return b + c;
3772         }
3773
3774         if (!p) {
3775             p = d * (.3 * 1.5);
3776         }
3777
3778         if (!a || a < Math.abs(c)) {
3779             a = c;
3780             var s = p / 4;
3781         }
3782         else {
3783             var s = p / (2 * Math.PI) * Math.asin(c / a);
3784         }
3785
3786         if (t < 1) {
3787             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3788                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3789         }
3790         return a * Math.pow(2, -10 * (t -= 1)) *
3791                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3792     },
3793
3794
3795
3796     backIn: function (t, b, c, d, s) {
3797         if (typeof s == 'undefined') {
3798             s = 1.70158;
3799         }
3800         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3801     },
3802
3803
3804     backOut: function (t, b, c, d, s) {
3805         if (typeof s == 'undefined') {
3806             s = 1.70158;
3807         }
3808         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3809     },
3810
3811
3812     backBoth: function (t, b, c, d, s) {
3813         if (typeof s == 'undefined') {
3814             s = 1.70158;
3815         }
3816
3817         if ((t /= d / 2 ) < 1) {
3818             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3819         }
3820         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3821     },
3822
3823
3824     bounceIn: function (t, b, c, d) {
3825         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3826     },
3827
3828
3829     bounceOut: function (t, b, c, d) {
3830         if ((t /= d) < (1 / 2.75)) {
3831             return c * (7.5625 * t * t) + b;
3832         } else if (t < (2 / 2.75)) {
3833             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3834         } else if (t < (2.5 / 2.75)) {
3835             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3836         }
3837         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3838     },
3839
3840
3841     bounceBoth: function (t, b, c, d) {
3842         if (t < d / 2) {
3843             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3844         }
3845         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3846     }
3847 };/*
3848  * Portions of this file are based on pieces of Yahoo User Interface Library
3849  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3850  * YUI licensed under the BSD License:
3851  * http://developer.yahoo.net/yui/license.txt
3852  * <script type="text/javascript">
3853  *
3854  */
3855     (function() {
3856         Roo.lib.Motion = function(el, attributes, duration, method) {
3857             if (el) {
3858                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3859             }
3860         };
3861
3862         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3863
3864
3865         var Y = Roo.lib;
3866         var superclass = Y.Motion.superclass;
3867         var proto = Y.Motion.prototype;
3868
3869         proto.toString = function() {
3870             var el = this.getEl();
3871             var id = el.id || el.tagName;
3872             return ("Motion " + id);
3873         };
3874
3875         proto.patterns.points = /^points$/i;
3876
3877         proto.setAttribute = function(attr, val, unit) {
3878             if (this.patterns.points.test(attr)) {
3879                 unit = unit || 'px';
3880                 superclass.setAttribute.call(this, 'left', val[0], unit);
3881                 superclass.setAttribute.call(this, 'top', val[1], unit);
3882             } else {
3883                 superclass.setAttribute.call(this, attr, val, unit);
3884             }
3885         };
3886
3887         proto.getAttribute = function(attr) {
3888             if (this.patterns.points.test(attr)) {
3889                 var val = [
3890                         superclass.getAttribute.call(this, 'left'),
3891                         superclass.getAttribute.call(this, 'top')
3892                         ];
3893             } else {
3894                 val = superclass.getAttribute.call(this, attr);
3895             }
3896
3897             return val;
3898         };
3899
3900         proto.doMethod = function(attr, start, end) {
3901             var val = null;
3902
3903             if (this.patterns.points.test(attr)) {
3904                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
3905                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
3906             } else {
3907                 val = superclass.doMethod.call(this, attr, start, end);
3908             }
3909             return val;
3910         };
3911
3912         proto.setRuntimeAttribute = function(attr) {
3913             if (this.patterns.points.test(attr)) {
3914                 var el = this.getEl();
3915                 var attributes = this.attributes;
3916                 var start;
3917                 var control = attributes['points']['control'] || [];
3918                 var end;
3919                 var i, len;
3920
3921                 if (control.length > 0 && !(control[0] instanceof Array)) {
3922                     control = [control];
3923                 } else {
3924                     var tmp = [];
3925                     for (i = 0,len = control.length; i < len; ++i) {
3926                         tmp[i] = control[i];
3927                     }
3928                     control = tmp;
3929                 }
3930
3931                 Roo.fly(el).position();
3932
3933                 if (isset(attributes['points']['from'])) {
3934                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
3935                 }
3936                 else {
3937                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
3938                 }
3939
3940                 start = this.getAttribute('points');
3941
3942
3943                 if (isset(attributes['points']['to'])) {
3944                     end = translateValues.call(this, attributes['points']['to'], start);
3945
3946                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
3947                     for (i = 0,len = control.length; i < len; ++i) {
3948                         control[i] = translateValues.call(this, control[i], start);
3949                     }
3950
3951
3952                 } else if (isset(attributes['points']['by'])) {
3953                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
3954
3955                     for (i = 0,len = control.length; i < len; ++i) {
3956                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
3957                     }
3958                 }
3959
3960                 this.runtimeAttributes[attr] = [start];
3961
3962                 if (control.length > 0) {
3963                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
3964                 }
3965
3966                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
3967             }
3968             else {
3969                 superclass.setRuntimeAttribute.call(this, attr);
3970             }
3971         };
3972
3973         var translateValues = function(val, start) {
3974             var pageXY = Roo.lib.Dom.getXY(this.getEl());
3975             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
3976
3977             return val;
3978         };
3979
3980         var isset = function(prop) {
3981             return (typeof prop !== 'undefined');
3982         };
3983     })();
3984 /*
3985  * Portions of this file are based on pieces of Yahoo User Interface Library
3986  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3987  * YUI licensed under the BSD License:
3988  * http://developer.yahoo.net/yui/license.txt
3989  * <script type="text/javascript">
3990  *
3991  */
3992     (function() {
3993         Roo.lib.Scroll = function(el, attributes, duration, method) {
3994             if (el) {
3995                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
3996             }
3997         };
3998
3999         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4000
4001
4002         var Y = Roo.lib;
4003         var superclass = Y.Scroll.superclass;
4004         var proto = Y.Scroll.prototype;
4005
4006         proto.toString = function() {
4007             var el = this.getEl();
4008             var id = el.id || el.tagName;
4009             return ("Scroll " + id);
4010         };
4011
4012         proto.doMethod = function(attr, start, end) {
4013             var val = null;
4014
4015             if (attr == 'scroll') {
4016                 val = [
4017                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4018                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4019                         ];
4020
4021             } else {
4022                 val = superclass.doMethod.call(this, attr, start, end);
4023             }
4024             return val;
4025         };
4026
4027         proto.getAttribute = function(attr) {
4028             var val = null;
4029             var el = this.getEl();
4030
4031             if (attr == 'scroll') {
4032                 val = [ el.scrollLeft, el.scrollTop ];
4033             } else {
4034                 val = superclass.getAttribute.call(this, attr);
4035             }
4036
4037             return val;
4038         };
4039
4040         proto.setAttribute = function(attr, val, unit) {
4041             var el = this.getEl();
4042
4043             if (attr == 'scroll') {
4044                 el.scrollLeft = val[0];
4045                 el.scrollTop = val[1];
4046             } else {
4047                 superclass.setAttribute.call(this, attr, val, unit);
4048             }
4049         };
4050     })();
4051 /*
4052  * Based on:
4053  * Ext JS Library 1.1.1
4054  * Copyright(c) 2006-2007, Ext JS, LLC.
4055  *
4056  * Originally Released Under LGPL - original licence link has changed is not relivant.
4057  *
4058  * Fork - LGPL
4059  * <script type="text/javascript">
4060  */
4061
4062
4063 // nasty IE9 hack - what a pile of crap that is..
4064
4065  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4066     Range.prototype.createContextualFragment = function (html) {
4067         var doc = window.document;
4068         var container = doc.createElement("div");
4069         container.innerHTML = html;
4070         var frag = doc.createDocumentFragment(), n;
4071         while ((n = container.firstChild)) {
4072             frag.appendChild(n);
4073         }
4074         return frag;
4075     };
4076 }
4077
4078 /**
4079  * @class Roo.DomHelper
4080  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4081  * For more information see <a href="http://www.jackslocum.com/yui/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">this blog post with examples</a>.
4082  * @singleton
4083  */
4084 Roo.DomHelper = function(){
4085     var tempTableEl = null;
4086     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4087     var tableRe = /^table|tbody|tr|td$/i;
4088     var xmlns = {};
4089     // build as innerHTML where available
4090     /** @ignore */
4091     var createHtml = function(o){
4092         if(typeof o == 'string'){
4093             return o;
4094         }
4095         var b = "";
4096         if(!o.tag){
4097             o.tag = "div";
4098         }
4099         b += "<" + o.tag;
4100         for(var attr in o){
4101             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") continue;
4102             if(attr == "style"){
4103                 var s = o["style"];
4104                 if(typeof s == "function"){
4105                     s = s.call();
4106                 }
4107                 if(typeof s == "string"){
4108                     b += ' style="' + s + '"';
4109                 }else if(typeof s == "object"){
4110                     b += ' style="';
4111                     for(var key in s){
4112                         if(typeof s[key] != "function"){
4113                             b += key + ":" + s[key] + ";";
4114                         }
4115                     }
4116                     b += '"';
4117                 }
4118             }else{
4119                 if(attr == "cls"){
4120                     b += ' class="' + o["cls"] + '"';
4121                 }else if(attr == "htmlFor"){
4122                     b += ' for="' + o["htmlFor"] + '"';
4123                 }else{
4124                     b += " " + attr + '="' + o[attr] + '"';
4125                 }
4126             }
4127         }
4128         if(emptyTags.test(o.tag)){
4129             b += "/>";
4130         }else{
4131             b += ">";
4132             var cn = o.children || o.cn;
4133             if(cn){
4134                 //http://bugs.kde.org/show_bug.cgi?id=71506
4135                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4136                     for(var i = 0, len = cn.length; i < len; i++) {
4137                         b += createHtml(cn[i], b);
4138                     }
4139                 }else{
4140                     b += createHtml(cn, b);
4141                 }
4142             }
4143             if(o.html){
4144                 b += o.html;
4145             }
4146             b += "</" + o.tag + ">";
4147         }
4148         return b;
4149     };
4150
4151     // build as dom
4152     /** @ignore */
4153     var createDom = function(o, parentNode){
4154          
4155         // defininition craeted..
4156         var ns = false;
4157         if (o.ns && o.ns != 'html') {
4158                
4159             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4160                 xmlns[o.ns] = o.xmlns;
4161                 ns = o.xmlns;
4162             }
4163             if (typeof(xmlns[o.ns]) == 'undefined') {
4164                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4165             }
4166             ns = xmlns[o.ns];
4167         }
4168         
4169         
4170         if (typeof(o) == 'string') {
4171             return parentNode.appendChild(document.createTextNode(o));
4172         }
4173         o.tag = o.tag || div;
4174         if (o.ns && Roo.isIE) {
4175             ns = false;
4176             o.tag = o.ns + ':' + o.tag;
4177             
4178         }
4179         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4180         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4181         for(var attr in o){
4182             
4183             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4184                     attr == "style" || typeof o[attr] == "function") continue;
4185                     
4186             if(attr=="cls" && Roo.isIE){
4187                 el.className = o["cls"];
4188             }else{
4189                 if(useSet) el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);
4190                 else el[attr] = o[attr];
4191             }
4192         }
4193         Roo.DomHelper.applyStyles(el, o.style);
4194         var cn = o.children || o.cn;
4195         if(cn){
4196             //http://bugs.kde.org/show_bug.cgi?id=71506
4197              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4198                 for(var i = 0, len = cn.length; i < len; i++) {
4199                     createDom(cn[i], el);
4200                 }
4201             }else{
4202                 createDom(cn, el);
4203             }
4204         }
4205         if(o.html){
4206             el.innerHTML = o.html;
4207         }
4208         if(parentNode){
4209            parentNode.appendChild(el);
4210         }
4211         return el;
4212     };
4213
4214     var ieTable = function(depth, s, h, e){
4215         tempTableEl.innerHTML = [s, h, e].join('');
4216         var i = -1, el = tempTableEl;
4217         while(++i < depth){
4218             el = el.firstChild;
4219         }
4220         return el;
4221     };
4222
4223     // kill repeat to save bytes
4224     var ts = '<table>',
4225         te = '</table>',
4226         tbs = ts+'<tbody>',
4227         tbe = '</tbody>'+te,
4228         trs = tbs + '<tr>',
4229         tre = '</tr>'+tbe;
4230
4231     /**
4232      * @ignore
4233      * Nasty code for IE's broken table implementation
4234      */
4235     var insertIntoTable = function(tag, where, el, html){
4236         if(!tempTableEl){
4237             tempTableEl = document.createElement('div');
4238         }
4239         var node;
4240         var before = null;
4241         if(tag == 'td'){
4242             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4243                 return;
4244             }
4245             if(where == 'beforebegin'){
4246                 before = el;
4247                 el = el.parentNode;
4248             } else{
4249                 before = el.nextSibling;
4250                 el = el.parentNode;
4251             }
4252             node = ieTable(4, trs, html, tre);
4253         }
4254         else if(tag == 'tr'){
4255             if(where == 'beforebegin'){
4256                 before = el;
4257                 el = el.parentNode;
4258                 node = ieTable(3, tbs, html, tbe);
4259             } else if(where == 'afterend'){
4260                 before = el.nextSibling;
4261                 el = el.parentNode;
4262                 node = ieTable(3, tbs, html, tbe);
4263             } else{ // INTO a TR
4264                 if(where == 'afterbegin'){
4265                     before = el.firstChild;
4266                 }
4267                 node = ieTable(4, trs, html, tre);
4268             }
4269         } else if(tag == 'tbody'){
4270             if(where == 'beforebegin'){
4271                 before = el;
4272                 el = el.parentNode;
4273                 node = ieTable(2, ts, html, te);
4274             } else if(where == 'afterend'){
4275                 before = el.nextSibling;
4276                 el = el.parentNode;
4277                 node = ieTable(2, ts, html, te);
4278             } else{
4279                 if(where == 'afterbegin'){
4280                     before = el.firstChild;
4281                 }
4282                 node = ieTable(3, tbs, html, tbe);
4283             }
4284         } else{ // TABLE
4285             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4286                 return;
4287             }
4288             if(where == 'afterbegin'){
4289                 before = el.firstChild;
4290             }
4291             node = ieTable(2, ts, html, te);
4292         }
4293         el.insertBefore(node, before);
4294         return node;
4295     };
4296
4297     return {
4298     /** True to force the use of DOM instead of html fragments @type Boolean */
4299     useDom : false,
4300
4301     /**
4302      * Returns the markup for the passed Element(s) config
4303      * @param {Object} o The Dom object spec (and children)
4304      * @return {String}
4305      */
4306     markup : function(o){
4307         return createHtml(o);
4308     },
4309
4310     /**
4311      * Applies a style specification to an element
4312      * @param {String/HTMLElement} el The element to apply styles to
4313      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4314      * a function which returns such a specification.
4315      */
4316     applyStyles : function(el, styles){
4317         if(styles){
4318            el = Roo.fly(el);
4319            if(typeof styles == "string"){
4320                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4321                var matches;
4322                while ((matches = re.exec(styles)) != null){
4323                    el.setStyle(matches[1], matches[2]);
4324                }
4325            }else if (typeof styles == "object"){
4326                for (var style in styles){
4327                   el.setStyle(style, styles[style]);
4328                }
4329            }else if (typeof styles == "function"){
4330                 Roo.DomHelper.applyStyles(el, styles.call());
4331            }
4332         }
4333     },
4334
4335     /**
4336      * Inserts an HTML fragment into the Dom
4337      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4338      * @param {HTMLElement} el The context element
4339      * @param {String} html The HTML fragmenet
4340      * @return {HTMLElement} The new node
4341      */
4342     insertHtml : function(where, el, html){
4343         where = where.toLowerCase();
4344         if(el.insertAdjacentHTML){
4345             if(tableRe.test(el.tagName)){
4346                 var rs;
4347                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4348                     return rs;
4349                 }
4350             }
4351             switch(where){
4352                 case "beforebegin":
4353                     el.insertAdjacentHTML('BeforeBegin', html);
4354                     return el.previousSibling;
4355                 case "afterbegin":
4356                     el.insertAdjacentHTML('AfterBegin', html);
4357                     return el.firstChild;
4358                 case "beforeend":
4359                     el.insertAdjacentHTML('BeforeEnd', html);
4360                     return el.lastChild;
4361                 case "afterend":
4362                     el.insertAdjacentHTML('AfterEnd', html);
4363                     return el.nextSibling;
4364             }
4365             throw 'Illegal insertion point -> "' + where + '"';
4366         }
4367         var range = el.ownerDocument.createRange();
4368         var frag;
4369         switch(where){
4370              case "beforebegin":
4371                 range.setStartBefore(el);
4372                 frag = range.createContextualFragment(html);
4373                 el.parentNode.insertBefore(frag, el);
4374                 return el.previousSibling;
4375              case "afterbegin":
4376                 if(el.firstChild){
4377                     range.setStartBefore(el.firstChild);
4378                     frag = range.createContextualFragment(html);
4379                     el.insertBefore(frag, el.firstChild);
4380                     return el.firstChild;
4381                 }else{
4382                     el.innerHTML = html;
4383                     return el.firstChild;
4384                 }
4385             case "beforeend":
4386                 if(el.lastChild){
4387                     range.setStartAfter(el.lastChild);
4388                     frag = range.createContextualFragment(html);
4389                     el.appendChild(frag);
4390                     return el.lastChild;
4391                 }else{
4392                     el.innerHTML = html;
4393                     return el.lastChild;
4394                 }
4395             case "afterend":
4396                 range.setStartAfter(el);
4397                 frag = range.createContextualFragment(html);
4398                 el.parentNode.insertBefore(frag, el.nextSibling);
4399                 return el.nextSibling;
4400             }
4401             throw 'Illegal insertion point -> "' + where + '"';
4402     },
4403
4404     /**
4405      * Creates new Dom element(s) and inserts them before el
4406      * @param {String/HTMLElement/Element} el The context element
4407      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4408      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4409      * @return {HTMLElement/Roo.Element} The new node
4410      */
4411     insertBefore : function(el, o, returnElement){
4412         return this.doInsert(el, o, returnElement, "beforeBegin");
4413     },
4414
4415     /**
4416      * Creates new Dom element(s) and inserts them after el
4417      * @param {String/HTMLElement/Element} el The context element
4418      * @param {Object} o The Dom object spec (and children)
4419      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4420      * @return {HTMLElement/Roo.Element} The new node
4421      */
4422     insertAfter : function(el, o, returnElement){
4423         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4424     },
4425
4426     /**
4427      * Creates new Dom element(s) and inserts them as the first child of el
4428      * @param {String/HTMLElement/Element} el The context element
4429      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4430      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4431      * @return {HTMLElement/Roo.Element} The new node
4432      */
4433     insertFirst : function(el, o, returnElement){
4434         return this.doInsert(el, o, returnElement, "afterBegin");
4435     },
4436
4437     // private
4438     doInsert : function(el, o, returnElement, pos, sibling){
4439         el = Roo.getDom(el);
4440         var newNode;
4441         if(this.useDom || o.ns){
4442             newNode = createDom(o, null);
4443             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4444         }else{
4445             var html = createHtml(o);
4446             newNode = this.insertHtml(pos, el, html);
4447         }
4448         return returnElement ? Roo.get(newNode, true) : newNode;
4449     },
4450
4451     /**
4452      * Creates new Dom element(s) and appends them to el
4453      * @param {String/HTMLElement/Element} el The context element
4454      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4455      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4456      * @return {HTMLElement/Roo.Element} The new node
4457      */
4458     append : function(el, o, returnElement){
4459         el = Roo.getDom(el);
4460         var newNode;
4461         if(this.useDom || o.ns){
4462             newNode = createDom(o, null);
4463             el.appendChild(newNode);
4464         }else{
4465             var html = createHtml(o);
4466             newNode = this.insertHtml("beforeEnd", el, html);
4467         }
4468         return returnElement ? Roo.get(newNode, true) : newNode;
4469     },
4470
4471     /**
4472      * Creates new Dom element(s) and overwrites the contents of el with them
4473      * @param {String/HTMLElement/Element} el The context element
4474      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4475      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4476      * @return {HTMLElement/Roo.Element} The new node
4477      */
4478     overwrite : function(el, o, returnElement){
4479         el = Roo.getDom(el);
4480         if (o.ns) {
4481           
4482             while (el.childNodes.length) {
4483                 el.removeChild(el.firstChild);
4484             }
4485             createDom(o, el);
4486         } else {
4487             el.innerHTML = createHtml(o);   
4488         }
4489         
4490         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4491     },
4492
4493     /**
4494      * Creates a new Roo.DomHelper.Template from the Dom object spec
4495      * @param {Object} o The Dom object spec (and children)
4496      * @return {Roo.DomHelper.Template} The new template
4497      */
4498     createTemplate : function(o){
4499         var html = createHtml(o);
4500         return new Roo.Template(html);
4501     }
4502     };
4503 }();
4504 /*
4505  * Based on:
4506  * Ext JS Library 1.1.1
4507  * Copyright(c) 2006-2007, Ext JS, LLC.
4508  *
4509  * Originally Released Under LGPL - original licence link has changed is not relivant.
4510  *
4511  * Fork - LGPL
4512  * <script type="text/javascript">
4513  */
4514  
4515 /**
4516 * @class Roo.Template
4517 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4518 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4519 * Usage:
4520 <pre><code>
4521 var t = new Roo.Template({
4522     html :  '&lt;div name="{id}"&gt;' + 
4523         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4524         '&lt;/div&gt;',
4525     myformat: function (value, allValues) {
4526         return 'XX' + value;
4527     }
4528 });
4529 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4530 </code></pre>
4531 * For more information see this blog post with examples: <a href="http://www.jackslocum.com/yui/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">DomHelper - Create Elements using DOM, HTML fragments and Templates</a>. 
4532 * @constructor
4533 * @param {Object} cfg - Configuration object.
4534 */
4535 Roo.Template = function(cfg){
4536     // BC!
4537     if(cfg instanceof Array){
4538         cfg = cfg.join("");
4539     }else if(arguments.length > 1){
4540         cfg = Array.prototype.join.call(arguments, "");
4541     }
4542     
4543     
4544     if (typeof(cfg) == 'object') {
4545         Roo.apply(this,cfg)
4546     } else {
4547         // bc
4548         this.html = cfg;
4549     }
4550     
4551     
4552 };
4553 Roo.Template.prototype = {
4554     
4555     /**
4556      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4557      */
4558     html : '',
4559     /**
4560      * Returns an HTML fragment of this template with the specified values applied.
4561      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4562      * @return {String} The HTML fragment
4563      */
4564     applyTemplate : function(values){
4565         try {
4566             
4567             if(this.compiled){
4568                 return this.compiled(values);
4569             }
4570             var useF = this.disableFormats !== true;
4571             var fm = Roo.util.Format, tpl = this;
4572             var fn = function(m, name, format, args){
4573                 if(format && useF){
4574                     if(format.substr(0, 5) == "this."){
4575                         return tpl.call(format.substr(5), values[name], values);
4576                     }else{
4577                         if(args){
4578                             // quoted values are required for strings in compiled templates, 
4579                             // but for non compiled we need to strip them
4580                             // quoted reversed for jsmin
4581                             var re = /^\s*['"](.*)["']\s*$/;
4582                             args = args.split(',');
4583                             for(var i = 0, len = args.length; i < len; i++){
4584                                 args[i] = args[i].replace(re, "$1");
4585                             }
4586                             args = [values[name]].concat(args);
4587                         }else{
4588                             args = [values[name]];
4589                         }
4590                         return fm[format].apply(fm, args);
4591                     }
4592                 }else{
4593                     return values[name] !== undefined ? values[name] : "";
4594                 }
4595             };
4596             return this.html.replace(this.re, fn);
4597         } catch (e) {
4598             Roo.log(e);
4599             throw e;
4600         }
4601          
4602     },
4603     
4604     /**
4605      * Sets the HTML used as the template and optionally compiles it.
4606      * @param {String} html
4607      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4608      * @return {Roo.Template} this
4609      */
4610     set : function(html, compile){
4611         this.html = html;
4612         this.compiled = null;
4613         if(compile){
4614             this.compile();
4615         }
4616         return this;
4617     },
4618     
4619     /**
4620      * True to disable format functions (defaults to false)
4621      * @type Boolean
4622      */
4623     disableFormats : false,
4624     
4625     /**
4626     * The regular expression used to match template variables 
4627     * @type RegExp
4628     * @property 
4629     */
4630     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4631     
4632     /**
4633      * Compiles the template into an internal function, eliminating the RegEx overhead.
4634      * @return {Roo.Template} this
4635      */
4636     compile : function(){
4637         var fm = Roo.util.Format;
4638         var useF = this.disableFormats !== true;
4639         var sep = Roo.isGecko ? "+" : ",";
4640         var fn = function(m, name, format, args){
4641             if(format && useF){
4642                 args = args ? ',' + args : "";
4643                 if(format.substr(0, 5) != "this."){
4644                     format = "fm." + format + '(';
4645                 }else{
4646                     format = 'this.call("'+ format.substr(5) + '", ';
4647                     args = ", values";
4648                 }
4649             }else{
4650                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4651             }
4652             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4653         };
4654         var body;
4655         // branched to use + in gecko and [].join() in others
4656         if(Roo.isGecko){
4657             body = "this.compiled = function(values){ return '" +
4658                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4659                     "';};";
4660         }else{
4661             body = ["this.compiled = function(values){ return ['"];
4662             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4663             body.push("'].join('');};");
4664             body = body.join('');
4665         }
4666         /**
4667          * eval:var:values
4668          * eval:var:fm
4669          */
4670         eval(body);
4671         return this;
4672     },
4673     
4674     // private function used to call members
4675     call : function(fnName, value, allValues){
4676         return this[fnName](value, allValues);
4677     },
4678     
4679     /**
4680      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4681      * @param {String/HTMLElement/Roo.Element} el The context element
4682      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4683      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4684      * @return {HTMLElement/Roo.Element} The new node or Element
4685      */
4686     insertFirst: function(el, values, returnElement){
4687         return this.doInsert('afterBegin', el, values, returnElement);
4688     },
4689
4690     /**
4691      * Applies the supplied values to the template and inserts the new node(s) before el.
4692      * @param {String/HTMLElement/Roo.Element} el The context element
4693      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4694      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4695      * @return {HTMLElement/Roo.Element} The new node or Element
4696      */
4697     insertBefore: function(el, values, returnElement){
4698         return this.doInsert('beforeBegin', el, values, returnElement);
4699     },
4700
4701     /**
4702      * Applies the supplied values to the template and inserts the new node(s) after el.
4703      * @param {String/HTMLElement/Roo.Element} el The context element
4704      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4705      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4706      * @return {HTMLElement/Roo.Element} The new node or Element
4707      */
4708     insertAfter : function(el, values, returnElement){
4709         return this.doInsert('afterEnd', el, values, returnElement);
4710     },
4711     
4712     /**
4713      * Applies the supplied values to the template and appends the new node(s) to el.
4714      * @param {String/HTMLElement/Roo.Element} el The context element
4715      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4716      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4717      * @return {HTMLElement/Roo.Element} The new node or Element
4718      */
4719     append : function(el, values, returnElement){
4720         return this.doInsert('beforeEnd', el, values, returnElement);
4721     },
4722
4723     doInsert : function(where, el, values, returnEl){
4724         el = Roo.getDom(el);
4725         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4726         return returnEl ? Roo.get(newNode, true) : newNode;
4727     },
4728
4729     /**
4730      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4731      * @param {String/HTMLElement/Roo.Element} el The context element
4732      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4733      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4734      * @return {HTMLElement/Roo.Element} The new node or Element
4735      */
4736     overwrite : function(el, values, returnElement){
4737         el = Roo.getDom(el);
4738         el.innerHTML = this.applyTemplate(values);
4739         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4740     }
4741 };
4742 /**
4743  * Alias for {@link #applyTemplate}
4744  * @method
4745  */
4746 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4747
4748 // backwards compat
4749 Roo.DomHelper.Template = Roo.Template;
4750
4751 /**
4752  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4753  * @param {String/HTMLElement} el A DOM element or its id
4754  * @returns {Roo.Template} The created template
4755  * @static
4756  */
4757 Roo.Template.from = function(el){
4758     el = Roo.getDom(el);
4759     return new Roo.Template(el.value || el.innerHTML);
4760 };/*
4761  * Based on:
4762  * Ext JS Library 1.1.1
4763  * Copyright(c) 2006-2007, Ext JS, LLC.
4764  *
4765  * Originally Released Under LGPL - original licence link has changed is not relivant.
4766  *
4767  * Fork - LGPL
4768  * <script type="text/javascript">
4769  */
4770  
4771
4772 /*
4773  * This is code is also distributed under MIT license for use
4774  * with jQuery and prototype JavaScript libraries.
4775  */
4776 /**
4777  * @class Roo.DomQuery
4778 Provides high performance selector/xpath processing by compiling queries into reusable functions. New pseudo classes and matchers can be plugged. It works on HTML and XML documents (if a content node is passed in).
4779 <p>
4780 DomQuery supports most of the <a href="http://www.w3.org/TR/2005/WD-css3-selectors-20051215/">CSS3 selectors spec</a>, along with some custom selectors and basic XPath.</p>
4781
4782 <p>
4783 All selectors, attribute filters and pseudos below can be combined infinitely in any order. For example "div.foo:nth-child(odd)[@foo=bar].bar:first" would be a perfectly valid selector. Node filters are processed in the order in which they appear, which allows you to optimize your queries for your document structure.
4784 </p>
4785 <h4>Element Selectors:</h4>
4786 <ul class="list">
4787     <li> <b>*</b> any element</li>
4788     <li> <b>E</b> an element with the tag E</li>
4789     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4790     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4791     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4792     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4793 </ul>
4794 <h4>Attribute Selectors:</h4>
4795 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4796 <ul class="list">
4797     <li> <b>E[foo]</b> has an attribute "foo"</li>
4798     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4799     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4800     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4801     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4802     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4803     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4804 </ul>
4805 <h4>Pseudo Classes:</h4>
4806 <ul class="list">
4807     <li> <b>E:first-child</b> E is the first child of its parent</li>
4808     <li> <b>E:last-child</b> E is the last child of its parent</li>
4809     <li> <b>E:nth-child(<i>n</i>)</b> E is the <i>n</i>th child of its parent (1 based as per the spec)</li>
4810     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4811     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4812     <li> <b>E:only-child</b> E is the only child of its parent</li>
4813     <li> <b>E:checked</b> E is an element that is has a checked attribute that is true (e.g. a radio or checkbox) </li>
4814     <li> <b>E:first</b> the first E in the resultset</li>
4815     <li> <b>E:last</b> the last E in the resultset</li>
4816     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4817     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4818     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4819     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4820     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4821     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4822     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4823     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4824     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4825 </ul>
4826 <h4>CSS Value Selectors:</h4>
4827 <ul class="list">
4828     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4829     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4830     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4831     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4832     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4833     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4834 </ul>
4835  * @singleton
4836  */
4837 Roo.DomQuery = function(){
4838     var cache = {}, simpleCache = {}, valueCache = {};
4839     var nonSpace = /\S/;
4840     var trimRe = /^\s+|\s+$/g;
4841     var tplRe = /\{(\d+)\}/g;
4842     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4843     var tagTokenRe = /^(#)?([\w-\*]+)/;
4844     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4845
4846     function child(p, index){
4847         var i = 0;
4848         var n = p.firstChild;
4849         while(n){
4850             if(n.nodeType == 1){
4851                if(++i == index){
4852                    return n;
4853                }
4854             }
4855             n = n.nextSibling;
4856         }
4857         return null;
4858     };
4859
4860     function next(n){
4861         while((n = n.nextSibling) && n.nodeType != 1);
4862         return n;
4863     };
4864
4865     function prev(n){
4866         while((n = n.previousSibling) && n.nodeType != 1);
4867         return n;
4868     };
4869
4870     function children(d){
4871         var n = d.firstChild, ni = -1;
4872             while(n){
4873                 var nx = n.nextSibling;
4874                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
4875                     d.removeChild(n);
4876                 }else{
4877                     n.nodeIndex = ++ni;
4878                 }
4879                 n = nx;
4880             }
4881             return this;
4882         };
4883
4884     function byClassName(c, a, v){
4885         if(!v){
4886             return c;
4887         }
4888         var r = [], ri = -1, cn;
4889         for(var i = 0, ci; ci = c[i]; i++){
4890             if((' '+ci.className+' ').indexOf(v) != -1){
4891                 r[++ri] = ci;
4892             }
4893         }
4894         return r;
4895     };
4896
4897     function attrValue(n, attr){
4898         if(!n.tagName && typeof n.length != "undefined"){
4899             n = n[0];
4900         }
4901         if(!n){
4902             return null;
4903         }
4904         if(attr == "for"){
4905             return n.htmlFor;
4906         }
4907         if(attr == "class" || attr == "className"){
4908             return n.className;
4909         }
4910         return n.getAttribute(attr) || n[attr];
4911
4912     };
4913
4914     function getNodes(ns, mode, tagName){
4915         var result = [], ri = -1, cs;
4916         if(!ns){
4917             return result;
4918         }
4919         tagName = tagName || "*";
4920         if(typeof ns.getElementsByTagName != "undefined"){
4921             ns = [ns];
4922         }
4923         if(!mode){
4924             for(var i = 0, ni; ni = ns[i]; i++){
4925                 cs = ni.getElementsByTagName(tagName);
4926                 for(var j = 0, ci; ci = cs[j]; j++){
4927                     result[++ri] = ci;
4928                 }
4929             }
4930         }else if(mode == "/" || mode == ">"){
4931             var utag = tagName.toUpperCase();
4932             for(var i = 0, ni, cn; ni = ns[i]; i++){
4933                 cn = ni.children || ni.childNodes;
4934                 for(var j = 0, cj; cj = cn[j]; j++){
4935                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
4936                         result[++ri] = cj;
4937                     }
4938                 }
4939             }
4940         }else if(mode == "+"){
4941             var utag = tagName.toUpperCase();
4942             for(var i = 0, n; n = ns[i]; i++){
4943                 while((n = n.nextSibling) && n.nodeType != 1);
4944                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
4945                     result[++ri] = n;
4946                 }
4947             }
4948         }else if(mode == "~"){
4949             for(var i = 0, n; n = ns[i]; i++){
4950                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
4951                 if(n){
4952                     result[++ri] = n;
4953                 }
4954             }
4955         }
4956         return result;
4957     };
4958
4959     function concat(a, b){
4960         if(b.slice){
4961             return a.concat(b);
4962         }
4963         for(var i = 0, l = b.length; i < l; i++){
4964             a[a.length] = b[i];
4965         }
4966         return a;
4967     }
4968
4969     function byTag(cs, tagName){
4970         if(cs.tagName || cs == document){
4971             cs = [cs];
4972         }
4973         if(!tagName){
4974             return cs;
4975         }
4976         var r = [], ri = -1;
4977         tagName = tagName.toLowerCase();
4978         for(var i = 0, ci; ci = cs[i]; i++){
4979             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
4980                 r[++ri] = ci;
4981             }
4982         }
4983         return r;
4984     };
4985
4986     function byId(cs, attr, id){
4987         if(cs.tagName || cs == document){
4988             cs = [cs];
4989         }
4990         if(!id){
4991             return cs;
4992         }
4993         var r = [], ri = -1;
4994         for(var i = 0,ci; ci = cs[i]; i++){
4995             if(ci && ci.id == id){
4996                 r[++ri] = ci;
4997                 return r;
4998             }
4999         }
5000         return r;
5001     };
5002
5003     function byAttribute(cs, attr, value, op, custom){
5004         var r = [], ri = -1, st = custom=="{";
5005         var f = Roo.DomQuery.operators[op];
5006         for(var i = 0, ci; ci = cs[i]; i++){
5007             var a;
5008             if(st){
5009                 a = Roo.DomQuery.getStyle(ci, attr);
5010             }
5011             else if(attr == "class" || attr == "className"){
5012                 a = ci.className;
5013             }else if(attr == "for"){
5014                 a = ci.htmlFor;
5015             }else if(attr == "href"){
5016                 a = ci.getAttribute("href", 2);
5017             }else{
5018                 a = ci.getAttribute(attr);
5019             }
5020             if((f && f(a, value)) || (!f && a)){
5021                 r[++ri] = ci;
5022             }
5023         }
5024         return r;
5025     };
5026
5027     function byPseudo(cs, name, value){
5028         return Roo.DomQuery.pseudos[name](cs, value);
5029     };
5030
5031     // This is for IE MSXML which does not support expandos.
5032     // IE runs the same speed using setAttribute, however FF slows way down
5033     // and Safari completely fails so they need to continue to use expandos.
5034     var isIE = window.ActiveXObject ? true : false;
5035
5036     // this eval is stop the compressor from
5037     // renaming the variable to something shorter
5038     
5039     /** eval:var:batch */
5040     var batch = 30803; 
5041
5042     var key = 30803;
5043
5044     function nodupIEXml(cs){
5045         var d = ++key;
5046         cs[0].setAttribute("_nodup", d);
5047         var r = [cs[0]];
5048         for(var i = 1, len = cs.length; i < len; i++){
5049             var c = cs[i];
5050             if(!c.getAttribute("_nodup") != d){
5051                 c.setAttribute("_nodup", d);
5052                 r[r.length] = c;
5053             }
5054         }
5055         for(var i = 0, len = cs.length; i < len; i++){
5056             cs[i].removeAttribute("_nodup");
5057         }
5058         return r;
5059     }
5060
5061     function nodup(cs){
5062         if(!cs){
5063             return [];
5064         }
5065         var len = cs.length, c, i, r = cs, cj, ri = -1;
5066         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5067             return cs;
5068         }
5069         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5070             return nodupIEXml(cs);
5071         }
5072         var d = ++key;
5073         cs[0]._nodup = d;
5074         for(i = 1; c = cs[i]; i++){
5075             if(c._nodup != d){
5076                 c._nodup = d;
5077             }else{
5078                 r = [];
5079                 for(var j = 0; j < i; j++){
5080                     r[++ri] = cs[j];
5081                 }
5082                 for(j = i+1; cj = cs[j]; j++){
5083                     if(cj._nodup != d){
5084                         cj._nodup = d;
5085                         r[++ri] = cj;
5086                     }
5087                 }
5088                 return r;
5089             }
5090         }
5091         return r;
5092     }
5093
5094     function quickDiffIEXml(c1, c2){
5095         var d = ++key;
5096         for(var i = 0, len = c1.length; i < len; i++){
5097             c1[i].setAttribute("_qdiff", d);
5098         }
5099         var r = [];
5100         for(var i = 0, len = c2.length; i < len; i++){
5101             if(c2[i].getAttribute("_qdiff") != d){
5102                 r[r.length] = c2[i];
5103             }
5104         }
5105         for(var i = 0, len = c1.length; i < len; i++){
5106            c1[i].removeAttribute("_qdiff");
5107         }
5108         return r;
5109     }
5110
5111     function quickDiff(c1, c2){
5112         var len1 = c1.length;
5113         if(!len1){
5114             return c2;
5115         }
5116         if(isIE && c1[0].selectSingleNode){
5117             return quickDiffIEXml(c1, c2);
5118         }
5119         var d = ++key;
5120         for(var i = 0; i < len1; i++){
5121             c1[i]._qdiff = d;
5122         }
5123         var r = [];
5124         for(var i = 0, len = c2.length; i < len; i++){
5125             if(c2[i]._qdiff != d){
5126                 r[r.length] = c2[i];
5127             }
5128         }
5129         return r;
5130     }
5131
5132     function quickId(ns, mode, root, id){
5133         if(ns == root){
5134            var d = root.ownerDocument || root;
5135            return d.getElementById(id);
5136         }
5137         ns = getNodes(ns, mode, "*");
5138         return byId(ns, null, id);
5139     }
5140
5141     return {
5142         getStyle : function(el, name){
5143             return Roo.fly(el).getStyle(name);
5144         },
5145         /**
5146          * Compiles a selector/xpath query into a reusable function. The returned function
5147          * takes one parameter "root" (optional), which is the context node from where the query should start.
5148          * @param {String} selector The selector/xpath query
5149          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5150          * @return {Function}
5151          */
5152         compile : function(path, type){
5153             type = type || "select";
5154             
5155             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5156             var q = path, mode, lq;
5157             var tk = Roo.DomQuery.matchers;
5158             var tklen = tk.length;
5159             var mm;
5160
5161             // accept leading mode switch
5162             var lmode = q.match(modeRe);
5163             if(lmode && lmode[1]){
5164                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5165                 q = q.replace(lmode[1], "");
5166             }
5167             // strip leading slashes
5168             while(path.substr(0, 1)=="/"){
5169                 path = path.substr(1);
5170             }
5171
5172             while(q && lq != q){
5173                 lq = q;
5174                 var tm = q.match(tagTokenRe);
5175                 if(type == "select"){
5176                     if(tm){
5177                         if(tm[1] == "#"){
5178                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5179                         }else{
5180                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5181                         }
5182                         q = q.replace(tm[0], "");
5183                     }else if(q.substr(0, 1) != '@'){
5184                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5185                     }
5186                 }else{
5187                     if(tm){
5188                         if(tm[1] == "#"){
5189                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5190                         }else{
5191                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5192                         }
5193                         q = q.replace(tm[0], "");
5194                     }
5195                 }
5196                 while(!(mm = q.match(modeRe))){
5197                     var matched = false;
5198                     for(var j = 0; j < tklen; j++){
5199                         var t = tk[j];
5200                         var m = q.match(t.re);
5201                         if(m){
5202                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5203                                                     return m[i];
5204                                                 });
5205                             q = q.replace(m[0], "");
5206                             matched = true;
5207                             break;
5208                         }
5209                     }
5210                     // prevent infinite loop on bad selector
5211                     if(!matched){
5212                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5213                     }
5214                 }
5215                 if(mm[1]){
5216                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5217                     q = q.replace(mm[1], "");
5218                 }
5219             }
5220             fn[fn.length] = "return nodup(n);\n}";
5221             
5222              /** 
5223               * list of variables that need from compression as they are used by eval.
5224              *  eval:var:batch 
5225              *  eval:var:nodup
5226              *  eval:var:byTag
5227              *  eval:var:ById
5228              *  eval:var:getNodes
5229              *  eval:var:quickId
5230              *  eval:var:mode
5231              *  eval:var:root
5232              *  eval:var:n
5233              *  eval:var:byClassName
5234              *  eval:var:byPseudo
5235              *  eval:var:byAttribute
5236              *  eval:var:attrValue
5237              * 
5238              **/ 
5239             eval(fn.join(""));
5240             return f;
5241         },
5242
5243         /**
5244          * Selects a group of elements.
5245          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5246          * @param {Node} root (optional) The start of the query (defaults to document).
5247          * @return {Array}
5248          */
5249         select : function(path, root, type){
5250             if(!root || root == document){
5251                 root = document;
5252             }
5253             if(typeof root == "string"){
5254                 root = document.getElementById(root);
5255             }
5256             var paths = path.split(",");
5257             var results = [];
5258             for(var i = 0, len = paths.length; i < len; i++){
5259                 var p = paths[i].replace(trimRe, "");
5260                 if(!cache[p]){
5261                     cache[p] = Roo.DomQuery.compile(p);
5262                     if(!cache[p]){
5263                         throw p + " is not a valid selector";
5264                     }
5265                 }
5266                 var result = cache[p](root);
5267                 if(result && result != document){
5268                     results = results.concat(result);
5269                 }
5270             }
5271             if(paths.length > 1){
5272                 return nodup(results);
5273             }
5274             return results;
5275         },
5276
5277         /**
5278          * Selects a single element.
5279          * @param {String} selector The selector/xpath query
5280          * @param {Node} root (optional) The start of the query (defaults to document).
5281          * @return {Element}
5282          */
5283         selectNode : function(path, root){
5284             return Roo.DomQuery.select(path, root)[0];
5285         },
5286
5287         /**
5288          * Selects the value of a node, optionally replacing null with the defaultValue.
5289          * @param {String} selector The selector/xpath query
5290          * @param {Node} root (optional) The start of the query (defaults to document).
5291          * @param {String} defaultValue
5292          */
5293         selectValue : function(path, root, defaultValue){
5294             path = path.replace(trimRe, "");
5295             if(!valueCache[path]){
5296                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5297             }
5298             var n = valueCache[path](root);
5299             n = n[0] ? n[0] : n;
5300             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5301             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5302         },
5303
5304         /**
5305          * Selects the value of a node, parsing integers and floats.
5306          * @param {String} selector The selector/xpath query
5307          * @param {Node} root (optional) The start of the query (defaults to document).
5308          * @param {Number} defaultValue
5309          * @return {Number}
5310          */
5311         selectNumber : function(path, root, defaultValue){
5312             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5313             return parseFloat(v);
5314         },
5315
5316         /**
5317          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5318          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5319          * @param {String} selector The simple selector to test
5320          * @return {Boolean}
5321          */
5322         is : function(el, ss){
5323             if(typeof el == "string"){
5324                 el = document.getElementById(el);
5325             }
5326             var isArray = (el instanceof Array);
5327             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5328             return isArray ? (result.length == el.length) : (result.length > 0);
5329         },
5330
5331         /**
5332          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5333          * @param {Array} el An array of elements to filter
5334          * @param {String} selector The simple selector to test
5335          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5336          * the selector instead of the ones that match
5337          * @return {Array}
5338          */
5339         filter : function(els, ss, nonMatches){
5340             ss = ss.replace(trimRe, "");
5341             if(!simpleCache[ss]){
5342                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5343             }
5344             var result = simpleCache[ss](els);
5345             return nonMatches ? quickDiff(result, els) : result;
5346         },
5347
5348         /**
5349          * Collection of matching regular expressions and code snippets.
5350          */
5351         matchers : [{
5352                 re: /^\.([\w-]+)/,
5353                 select: 'n = byClassName(n, null, " {1} ");'
5354             }, {
5355                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5356                 select: 'n = byPseudo(n, "{1}", "{2}");'
5357             },{
5358                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5359                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5360             }, {
5361                 re: /^#([\w-]+)/,
5362                 select: 'n = byId(n, null, "{1}");'
5363             },{
5364                 re: /^@([\w-]+)/,
5365                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5366             }
5367         ],
5368
5369         /**
5370          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5371          * New operators can be added as long as the match the format <i>c</i>= where <i>c</i> is any character other than space, &gt; &lt;.
5372          */
5373         operators : {
5374             "=" : function(a, v){
5375                 return a == v;
5376             },
5377             "!=" : function(a, v){
5378                 return a != v;
5379             },
5380             "^=" : function(a, v){
5381                 return a && a.substr(0, v.length) == v;
5382             },
5383             "$=" : function(a, v){
5384                 return a && a.substr(a.length-v.length) == v;
5385             },
5386             "*=" : function(a, v){
5387                 return a && a.indexOf(v) !== -1;
5388             },
5389             "%=" : function(a, v){
5390                 return (a % v) == 0;
5391             },
5392             "|=" : function(a, v){
5393                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5394             },
5395             "~=" : function(a, v){
5396                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5397             }
5398         },
5399
5400         /**
5401          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5402          * and the argument (if any) supplied in the selector.
5403          */
5404         pseudos : {
5405             "first-child" : function(c){
5406                 var r = [], ri = -1, n;
5407                 for(var i = 0, ci; ci = n = c[i]; i++){
5408                     while((n = n.previousSibling) && n.nodeType != 1);
5409                     if(!n){
5410                         r[++ri] = ci;
5411                     }
5412                 }
5413                 return r;
5414             },
5415
5416             "last-child" : function(c){
5417                 var r = [], ri = -1, n;
5418                 for(var i = 0, ci; ci = n = c[i]; i++){
5419                     while((n = n.nextSibling) && n.nodeType != 1);
5420                     if(!n){
5421                         r[++ri] = ci;
5422                     }
5423                 }
5424                 return r;
5425             },
5426
5427             "nth-child" : function(c, a) {
5428                 var r = [], ri = -1;
5429                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5430                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5431                 for(var i = 0, n; n = c[i]; i++){
5432                     var pn = n.parentNode;
5433                     if (batch != pn._batch) {
5434                         var j = 0;
5435                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5436                             if(cn.nodeType == 1){
5437                                cn.nodeIndex = ++j;
5438                             }
5439                         }
5440                         pn._batch = batch;
5441                     }
5442                     if (f == 1) {
5443                         if (l == 0 || n.nodeIndex == l){
5444                             r[++ri] = n;
5445                         }
5446                     } else if ((n.nodeIndex + l) % f == 0){
5447                         r[++ri] = n;
5448                     }
5449                 }
5450
5451                 return r;
5452             },
5453
5454             "only-child" : function(c){
5455                 var r = [], ri = -1;;
5456                 for(var i = 0, ci; ci = c[i]; i++){
5457                     if(!prev(ci) && !next(ci)){
5458                         r[++ri] = ci;
5459                     }
5460                 }
5461                 return r;
5462             },
5463
5464             "empty" : function(c){
5465                 var r = [], ri = -1;
5466                 for(var i = 0, ci; ci = c[i]; i++){
5467                     var cns = ci.childNodes, j = 0, cn, empty = true;
5468                     while(cn = cns[j]){
5469                         ++j;
5470                         if(cn.nodeType == 1 || cn.nodeType == 3){
5471                             empty = false;
5472                             break;
5473                         }
5474                     }
5475                     if(empty){
5476                         r[++ri] = ci;
5477                     }
5478                 }
5479                 return r;
5480             },
5481
5482             "contains" : function(c, v){
5483                 var r = [], ri = -1;
5484                 for(var i = 0, ci; ci = c[i]; i++){
5485                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5486                         r[++ri] = ci;
5487                     }
5488                 }
5489                 return r;
5490             },
5491
5492             "nodeValue" : function(c, v){
5493                 var r = [], ri = -1;
5494                 for(var i = 0, ci; ci = c[i]; i++){
5495                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5496                         r[++ri] = ci;
5497                     }
5498                 }
5499                 return r;
5500             },
5501
5502             "checked" : function(c){
5503                 var r = [], ri = -1;
5504                 for(var i = 0, ci; ci = c[i]; i++){
5505                     if(ci.checked == true){
5506                         r[++ri] = ci;
5507                     }
5508                 }
5509                 return r;
5510             },
5511
5512             "not" : function(c, ss){
5513                 return Roo.DomQuery.filter(c, ss, true);
5514             },
5515
5516             "odd" : function(c){
5517                 return this["nth-child"](c, "odd");
5518             },
5519
5520             "even" : function(c){
5521                 return this["nth-child"](c, "even");
5522             },
5523
5524             "nth" : function(c, a){
5525                 return c[a-1] || [];
5526             },
5527
5528             "first" : function(c){
5529                 return c[0] || [];
5530             },
5531
5532             "last" : function(c){
5533                 return c[c.length-1] || [];
5534             },
5535
5536             "has" : function(c, ss){
5537                 var s = Roo.DomQuery.select;
5538                 var r = [], ri = -1;
5539                 for(var i = 0, ci; ci = c[i]; i++){
5540                     if(s(ss, ci).length > 0){
5541                         r[++ri] = ci;
5542                     }
5543                 }
5544                 return r;
5545             },
5546
5547             "next" : function(c, ss){
5548                 var is = Roo.DomQuery.is;
5549                 var r = [], ri = -1;
5550                 for(var i = 0, ci; ci = c[i]; i++){
5551                     var n = next(ci);
5552                     if(n && is(n, ss)){
5553                         r[++ri] = ci;
5554                     }
5555                 }
5556                 return r;
5557             },
5558
5559             "prev" : function(c, ss){
5560                 var is = Roo.DomQuery.is;
5561                 var r = [], ri = -1;
5562                 for(var i = 0, ci; ci = c[i]; i++){
5563                     var n = prev(ci);
5564                     if(n && is(n, ss)){
5565                         r[++ri] = ci;
5566                     }
5567                 }
5568                 return r;
5569             }
5570         }
5571     };
5572 }();
5573
5574 /**
5575  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5576  * @param {String} path The selector/xpath query
5577  * @param {Node} root (optional) The start of the query (defaults to document).
5578  * @return {Array}
5579  * @member Roo
5580  * @method query
5581  */
5582 Roo.query = Roo.DomQuery.select;
5583 /*
5584  * Based on:
5585  * Ext JS Library 1.1.1
5586  * Copyright(c) 2006-2007, Ext JS, LLC.
5587  *
5588  * Originally Released Under LGPL - original licence link has changed is not relivant.
5589  *
5590  * Fork - LGPL
5591  * <script type="text/javascript">
5592  */
5593
5594 /**
5595  * @class Roo.util.Observable
5596  * Base class that provides a common interface for publishing events. Subclasses are expected to
5597  * to have a property "events" with all the events defined.<br>
5598  * For example:
5599  * <pre><code>
5600  Employee = function(name){
5601     this.name = name;
5602     this.addEvents({
5603         "fired" : true,
5604         "quit" : true
5605     });
5606  }
5607  Roo.extend(Employee, Roo.util.Observable);
5608 </code></pre>
5609  * @param {Object} config properties to use (incuding events / listeners)
5610  */
5611
5612 Roo.util.Observable = function(cfg){
5613     
5614     cfg = cfg|| {};
5615     this.addEvents(cfg.events || {});
5616     if (cfg.events) {
5617         delete cfg.events; // make sure
5618     }
5619      
5620     Roo.apply(this, cfg);
5621     
5622     if(this.listeners){
5623         this.on(this.listeners);
5624         delete this.listeners;
5625     }
5626 };
5627 Roo.util.Observable.prototype = {
5628     /** 
5629  * @cfg {Object} listeners  list of events and functions to call for this object, 
5630  * For example :
5631  * <pre><code>
5632     listeners :  { 
5633        'click' : function(e) {
5634            ..... 
5635         } ,
5636         .... 
5637     } 
5638   </code></pre>
5639  */
5640     
5641     
5642     /**
5643      * Fires the specified event with the passed parameters (minus the event name).
5644      * @param {String} eventName
5645      * @param {Object...} args Variable number of parameters are passed to handlers
5646      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5647      */
5648     fireEvent : function(){
5649         var ce = this.events[arguments[0].toLowerCase()];
5650         if(typeof ce == "object"){
5651             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5652         }else{
5653             return true;
5654         }
5655     },
5656
5657     // private
5658     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5659
5660     /**
5661      * Appends an event handler to this component
5662      * @param {String}   eventName The type of event to listen for
5663      * @param {Function} handler The method the event invokes
5664      * @param {Object}   scope (optional) The scope in which to execute the handler
5665      * function. The handler function's "this" context.
5666      * @param {Object}   options (optional) An object containing handler configuration
5667      * properties. This may contain any of the following properties:<ul>
5668      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5669      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5670      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5671      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5672      * by the specified number of milliseconds. If the event fires again within that time, the original
5673      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5674      * </ul><br>
5675      * <p>
5676      * <b>Combining Options</b><br>
5677      * Using the options argument, it is possible to combine different types of listeners:<br>
5678      * <br>
5679      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5680                 <pre><code>
5681                 el.on('click', this.onClick, this, {
5682                         single: true,
5683                 delay: 100,
5684                 forumId: 4
5685                 });
5686                 </code></pre>
5687      * <p>
5688      * <b>Attaching multiple handlers in 1 call</b><br>
5689      * The method also allows for a single argument to be passed which is a config object containing properties
5690      * which specify multiple handlers.
5691      * <pre><code>
5692                 el.on({
5693                         'click': {
5694                         fn: this.onClick,
5695                         scope: this,
5696                         delay: 100
5697                 }, 
5698                 'mouseover': {
5699                         fn: this.onMouseOver,
5700                         scope: this
5701                 },
5702                 'mouseout': {
5703                         fn: this.onMouseOut,
5704                         scope: this
5705                 }
5706                 });
5707                 </code></pre>
5708      * <p>
5709      * Or a shorthand syntax which passes the same scope object to all handlers:
5710         <pre><code>
5711                 el.on({
5712                         'click': this.onClick,
5713                 'mouseover': this.onMouseOver,
5714                 'mouseout': this.onMouseOut,
5715                 scope: this
5716                 });
5717                 </code></pre>
5718      */
5719     addListener : function(eventName, fn, scope, o){
5720         if(typeof eventName == "object"){
5721             o = eventName;
5722             for(var e in o){
5723                 if(this.filterOptRe.test(e)){
5724                     continue;
5725                 }
5726                 if(typeof o[e] == "function"){
5727                     // shared options
5728                     this.addListener(e, o[e], o.scope,  o);
5729                 }else{
5730                     // individual options
5731                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5732                 }
5733             }
5734             return;
5735         }
5736         o = (!o || typeof o == "boolean") ? {} : o;
5737         eventName = eventName.toLowerCase();
5738         var ce = this.events[eventName] || true;
5739         if(typeof ce == "boolean"){
5740             ce = new Roo.util.Event(this, eventName);
5741             this.events[eventName] = ce;
5742         }
5743         ce.addListener(fn, scope, o);
5744     },
5745
5746     /**
5747      * Removes a listener
5748      * @param {String}   eventName     The type of event to listen for
5749      * @param {Function} handler        The handler to remove
5750      * @param {Object}   scope  (optional) The scope (this object) for the handler
5751      */
5752     removeListener : function(eventName, fn, scope){
5753         var ce = this.events[eventName.toLowerCase()];
5754         if(typeof ce == "object"){
5755             ce.removeListener(fn, scope);
5756         }
5757     },
5758
5759     /**
5760      * Removes all listeners for this object
5761      */
5762     purgeListeners : function(){
5763         for(var evt in this.events){
5764             if(typeof this.events[evt] == "object"){
5765                  this.events[evt].clearListeners();
5766             }
5767         }
5768     },
5769
5770     relayEvents : function(o, events){
5771         var createHandler = function(ename){
5772             return function(){
5773                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5774             };
5775         };
5776         for(var i = 0, len = events.length; i < len; i++){
5777             var ename = events[i];
5778             if(!this.events[ename]){ this.events[ename] = true; };
5779             o.on(ename, createHandler(ename), this);
5780         }
5781     },
5782
5783     /**
5784      * Used to define events on this Observable
5785      * @param {Object} object The object with the events defined
5786      */
5787     addEvents : function(o){
5788         if(!this.events){
5789             this.events = {};
5790         }
5791         Roo.applyIf(this.events, o);
5792     },
5793
5794     /**
5795      * Checks to see if this object has any listeners for a specified event
5796      * @param {String} eventName The name of the event to check for
5797      * @return {Boolean} True if the event is being listened for, else false
5798      */
5799     hasListener : function(eventName){
5800         var e = this.events[eventName];
5801         return typeof e == "object" && e.listeners.length > 0;
5802     }
5803 };
5804 /**
5805  * Appends an event handler to this element (shorthand for addListener)
5806  * @param {String}   eventName     The type of event to listen for
5807  * @param {Function} handler        The method the event invokes
5808  * @param {Object}   scope (optional) The scope in which to execute the handler
5809  * function. The handler function's "this" context.
5810  * @param {Object}   options  (optional)
5811  * @method
5812  */
5813 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5814 /**
5815  * Removes a listener (shorthand for removeListener)
5816  * @param {String}   eventName     The type of event to listen for
5817  * @param {Function} handler        The handler to remove
5818  * @param {Object}   scope  (optional) The scope (this object) for the handler
5819  * @method
5820  */
5821 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5822
5823 /**
5824  * Starts capture on the specified Observable. All events will be passed
5825  * to the supplied function with the event name + standard signature of the event
5826  * <b>before</b> the event is fired. If the supplied function returns false,
5827  * the event will not fire.
5828  * @param {Observable} o The Observable to capture
5829  * @param {Function} fn The function to call
5830  * @param {Object} scope (optional) The scope (this object) for the fn
5831  * @static
5832  */
5833 Roo.util.Observable.capture = function(o, fn, scope){
5834     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5835 };
5836
5837 /**
5838  * Removes <b>all</b> added captures from the Observable.
5839  * @param {Observable} o The Observable to release
5840  * @static
5841  */
5842 Roo.util.Observable.releaseCapture = function(o){
5843     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5844 };
5845
5846 (function(){
5847
5848     var createBuffered = function(h, o, scope){
5849         var task = new Roo.util.DelayedTask();
5850         return function(){
5851             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5852         };
5853     };
5854
5855     var createSingle = function(h, e, fn, scope){
5856         return function(){
5857             e.removeListener(fn, scope);
5858             return h.apply(scope, arguments);
5859         };
5860     };
5861
5862     var createDelayed = function(h, o, scope){
5863         return function(){
5864             var args = Array.prototype.slice.call(arguments, 0);
5865             setTimeout(function(){
5866                 h.apply(scope, args);
5867             }, o.delay || 10);
5868         };
5869     };
5870
5871     Roo.util.Event = function(obj, name){
5872         this.name = name;
5873         this.obj = obj;
5874         this.listeners = [];
5875     };
5876
5877     Roo.util.Event.prototype = {
5878         addListener : function(fn, scope, options){
5879             var o = options || {};
5880             scope = scope || this.obj;
5881             if(!this.isListening(fn, scope)){
5882                 var l = {fn: fn, scope: scope, options: o};
5883                 var h = fn;
5884                 if(o.delay){
5885                     h = createDelayed(h, o, scope);
5886                 }
5887                 if(o.single){
5888                     h = createSingle(h, this, fn, scope);
5889                 }
5890                 if(o.buffer){
5891                     h = createBuffered(h, o, scope);
5892                 }
5893                 l.fireFn = h;
5894                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
5895                     this.listeners.push(l);
5896                 }else{
5897                     this.listeners = this.listeners.slice(0);
5898                     this.listeners.push(l);
5899                 }
5900             }
5901         },
5902
5903         findListener : function(fn, scope){
5904             scope = scope || this.obj;
5905             var ls = this.listeners;
5906             for(var i = 0, len = ls.length; i < len; i++){
5907                 var l = ls[i];
5908                 if(l.fn == fn && l.scope == scope){
5909                     return i;
5910                 }
5911             }
5912             return -1;
5913         },
5914
5915         isListening : function(fn, scope){
5916             return this.findListener(fn, scope) != -1;
5917         },
5918
5919         removeListener : function(fn, scope){
5920             var index;
5921             if((index = this.findListener(fn, scope)) != -1){
5922                 if(!this.firing){
5923                     this.listeners.splice(index, 1);
5924                 }else{
5925                     this.listeners = this.listeners.slice(0);
5926                     this.listeners.splice(index, 1);
5927                 }
5928                 return true;
5929             }
5930             return false;
5931         },
5932
5933         clearListeners : function(){
5934             this.listeners = [];
5935         },
5936
5937         fire : function(){
5938             var ls = this.listeners, scope, len = ls.length;
5939             if(len > 0){
5940                 this.firing = true;
5941                 var args = Array.prototype.slice.call(arguments, 0);
5942                 for(var i = 0; i < len; i++){
5943                     var l = ls[i];
5944                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
5945                         this.firing = false;
5946                         return false;
5947                     }
5948                 }
5949                 this.firing = false;
5950             }
5951             return true;
5952         }
5953     };
5954 })();/*
5955  * Based on:
5956  * Ext JS Library 1.1.1
5957  * Copyright(c) 2006-2007, Ext JS, LLC.
5958  *
5959  * Originally Released Under LGPL - original licence link has changed is not relivant.
5960  *
5961  * Fork - LGPL
5962  * <script type="text/javascript">
5963  */
5964
5965 /**
5966  * @class Roo.EventManager
5967  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
5968  * several useful events directly.
5969  * See {@link Roo.EventObject} for more details on normalized event objects.
5970  * @singleton
5971  */
5972 Roo.EventManager = function(){
5973     var docReadyEvent, docReadyProcId, docReadyState = false;
5974     var resizeEvent, resizeTask, textEvent, textSize;
5975     var E = Roo.lib.Event;
5976     var D = Roo.lib.Dom;
5977
5978
5979     var fireDocReady = function(){
5980         if(!docReadyState){
5981             docReadyState = true;
5982             Roo.isReady = true;
5983             if(docReadyProcId){
5984                 clearInterval(docReadyProcId);
5985             }
5986             if(Roo.isGecko || Roo.isOpera) {
5987                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
5988             }
5989             if(Roo.isIE){
5990                 var defer = document.getElementById("ie-deferred-loader");
5991                 if(defer){
5992                     defer.onreadystatechange = null;
5993                     defer.parentNode.removeChild(defer);
5994                 }
5995             }
5996             if(docReadyEvent){
5997                 docReadyEvent.fire();
5998                 docReadyEvent.clearListeners();
5999             }
6000         }
6001     };
6002     
6003     var initDocReady = function(){
6004         docReadyEvent = new Roo.util.Event();
6005         if(Roo.isGecko || Roo.isOpera) {
6006             document.addEventListener("DOMContentLoaded", fireDocReady, false);
6007         }else if(Roo.isIE){
6008             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
6009             var defer = document.getElementById("ie-deferred-loader");
6010             defer.onreadystatechange = function(){
6011                 if(this.readyState == "complete"){
6012                     fireDocReady();
6013                 }
6014             };
6015         }else if(Roo.isSafari){ 
6016             docReadyProcId = setInterval(function(){
6017                 var rs = document.readyState;
6018                 if(rs == "complete") {
6019                     fireDocReady();     
6020                  }
6021             }, 10);
6022         }
6023         // no matter what, make sure it fires on load
6024         E.on(window, "load", fireDocReady);
6025     };
6026
6027     var createBuffered = function(h, o){
6028         var task = new Roo.util.DelayedTask(h);
6029         return function(e){
6030             // create new event object impl so new events don't wipe out properties
6031             e = new Roo.EventObjectImpl(e);
6032             task.delay(o.buffer, h, null, [e]);
6033         };
6034     };
6035
6036     var createSingle = function(h, el, ename, fn){
6037         return function(e){
6038             Roo.EventManager.removeListener(el, ename, fn);
6039             h(e);
6040         };
6041     };
6042
6043     var createDelayed = function(h, o){
6044         return function(e){
6045             // create new event object impl so new events don't wipe out properties
6046             e = new Roo.EventObjectImpl(e);
6047             setTimeout(function(){
6048                 h(e);
6049             }, o.delay || 10);
6050         };
6051     };
6052
6053     var listen = function(element, ename, opt, fn, scope){
6054         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6055         fn = fn || o.fn; scope = scope || o.scope;
6056         var el = Roo.getDom(element);
6057         if(!el){
6058             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6059         }
6060         var h = function(e){
6061             e = Roo.EventObject.setEvent(e);
6062             var t;
6063             if(o.delegate){
6064                 t = e.getTarget(o.delegate, el);
6065                 if(!t){
6066                     return;
6067                 }
6068             }else{
6069                 t = e.target;
6070             }
6071             if(o.stopEvent === true){
6072                 e.stopEvent();
6073             }
6074             if(o.preventDefault === true){
6075                e.preventDefault();
6076             }
6077             if(o.stopPropagation === true){
6078                 e.stopPropagation();
6079             }
6080
6081             if(o.normalized === false){
6082                 e = e.browserEvent;
6083             }
6084
6085             fn.call(scope || el, e, t, o);
6086         };
6087         if(o.delay){
6088             h = createDelayed(h, o);
6089         }
6090         if(o.single){
6091             h = createSingle(h, el, ename, fn);
6092         }
6093         if(o.buffer){
6094             h = createBuffered(h, o);
6095         }
6096         fn._handlers = fn._handlers || [];
6097         fn._handlers.push([Roo.id(el), ename, h]);
6098
6099         E.on(el, ename, h);
6100         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6101             el.addEventListener("DOMMouseScroll", h, false);
6102             E.on(window, 'unload', function(){
6103                 el.removeEventListener("DOMMouseScroll", h, false);
6104             });
6105         }
6106         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6107             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6108         }
6109         return h;
6110     };
6111
6112     var stopListening = function(el, ename, fn){
6113         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6114         if(hds){
6115             for(var i = 0, len = hds.length; i < len; i++){
6116                 var h = hds[i];
6117                 if(h[0] == id && h[1] == ename){
6118                     hd = h[2];
6119                     hds.splice(i, 1);
6120                     break;
6121                 }
6122             }
6123         }
6124         E.un(el, ename, hd);
6125         el = Roo.getDom(el);
6126         if(ename == "mousewheel" && el.addEventListener){
6127             el.removeEventListener("DOMMouseScroll", hd, false);
6128         }
6129         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6130             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6131         }
6132     };
6133
6134     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6135     
6136     var pub = {
6137         
6138         
6139         /** 
6140          * Fix for doc tools
6141          * @scope Roo.EventManager
6142          */
6143         
6144         
6145         /** 
6146          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6147          * object with a Roo.EventObject
6148          * @param {Function} fn        The method the event invokes
6149          * @param {Object}   scope    An object that becomes the scope of the handler
6150          * @param {boolean}  override If true, the obj passed in becomes
6151          *                             the execution scope of the listener
6152          * @return {Function} The wrapped function
6153          * @deprecated
6154          */
6155         wrap : function(fn, scope, override){
6156             return function(e){
6157                 Roo.EventObject.setEvent(e);
6158                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6159             };
6160         },
6161         
6162         /**
6163      * Appends an event handler to an element (shorthand for addListener)
6164      * @param {String/HTMLElement}   element        The html element or id to assign the
6165      * @param {String}   eventName The type of event to listen for
6166      * @param {Function} handler The method the event invokes
6167      * @param {Object}   scope (optional) The scope in which to execute the handler
6168      * function. The handler function's "this" context.
6169      * @param {Object}   options (optional) An object containing handler configuration
6170      * properties. This may contain any of the following properties:<ul>
6171      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6172      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6173      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6174      * <li>preventDefault {Boolean} True to prevent the default action</li>
6175      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6176      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6177      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6178      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6179      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6180      * by the specified number of milliseconds. If the event fires again within that time, the original
6181      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6182      * </ul><br>
6183      * <p>
6184      * <b>Combining Options</b><br>
6185      * Using the options argument, it is possible to combine different types of listeners:<br>
6186      * <br>
6187      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6188      * Code:<pre><code>
6189 el.on('click', this.onClick, this, {
6190     single: true,
6191     delay: 100,
6192     stopEvent : true,
6193     forumId: 4
6194 });</code></pre>
6195      * <p>
6196      * <b>Attaching multiple handlers in 1 call</b><br>
6197       * The method also allows for a single argument to be passed which is a config object containing properties
6198      * which specify multiple handlers.
6199      * <p>
6200      * Code:<pre><code>
6201 el.on({
6202     'click' : {
6203         fn: this.onClick
6204         scope: this,
6205         delay: 100
6206     },
6207     'mouseover' : {
6208         fn: this.onMouseOver
6209         scope: this
6210     },
6211     'mouseout' : {
6212         fn: this.onMouseOut
6213         scope: this
6214     }
6215 });</code></pre>
6216      * <p>
6217      * Or a shorthand syntax:<br>
6218      * Code:<pre><code>
6219 el.on({
6220     'click' : this.onClick,
6221     'mouseover' : this.onMouseOver,
6222     'mouseout' : this.onMouseOut
6223     scope: this
6224 });</code></pre>
6225      */
6226         addListener : function(element, eventName, fn, scope, options){
6227             if(typeof eventName == "object"){
6228                 var o = eventName;
6229                 for(var e in o){
6230                     if(propRe.test(e)){
6231                         continue;
6232                     }
6233                     if(typeof o[e] == "function"){
6234                         // shared options
6235                         listen(element, e, o, o[e], o.scope);
6236                     }else{
6237                         // individual options
6238                         listen(element, e, o[e]);
6239                     }
6240                 }
6241                 return;
6242             }
6243             return listen(element, eventName, options, fn, scope);
6244         },
6245         
6246         /**
6247          * Removes an event handler
6248          *
6249          * @param {String/HTMLElement}   element        The id or html element to remove the 
6250          *                             event from
6251          * @param {String}   eventName     The type of event
6252          * @param {Function} fn
6253          * @return {Boolean} True if a listener was actually removed
6254          */
6255         removeListener : function(element, eventName, fn){
6256             return stopListening(element, eventName, fn);
6257         },
6258         
6259         /**
6260          * Fires when the document is ready (before onload and before images are loaded). Can be 
6261          * accessed shorthanded Roo.onReady().
6262          * @param {Function} fn        The method the event invokes
6263          * @param {Object}   scope    An  object that becomes the scope of the handler
6264          * @param {boolean}  options
6265          */
6266         onDocumentReady : function(fn, scope, options){
6267             if(docReadyState){ // if it already fired
6268                 docReadyEvent.addListener(fn, scope, options);
6269                 docReadyEvent.fire();
6270                 docReadyEvent.clearListeners();
6271                 return;
6272             }
6273             if(!docReadyEvent){
6274                 initDocReady();
6275             }
6276             docReadyEvent.addListener(fn, scope, options);
6277         },
6278         
6279         /**
6280          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6281          * @param {Function} fn        The method the event invokes
6282          * @param {Object}   scope    An object that becomes the scope of the handler
6283          * @param {boolean}  options
6284          */
6285         onWindowResize : function(fn, scope, options){
6286             if(!resizeEvent){
6287                 resizeEvent = new Roo.util.Event();
6288                 resizeTask = new Roo.util.DelayedTask(function(){
6289                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6290                 });
6291                 E.on(window, "resize", function(){
6292                     if(Roo.isIE){
6293                         resizeTask.delay(50);
6294                     }else{
6295                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6296                     }
6297                 });
6298             }
6299             resizeEvent.addListener(fn, scope, options);
6300         },
6301
6302         /**
6303          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6304          * @param {Function} fn        The method the event invokes
6305          * @param {Object}   scope    An object that becomes the scope of the handler
6306          * @param {boolean}  options
6307          */
6308         onTextResize : function(fn, scope, options){
6309             if(!textEvent){
6310                 textEvent = new Roo.util.Event();
6311                 var textEl = new Roo.Element(document.createElement('div'));
6312                 textEl.dom.className = 'x-text-resize';
6313                 textEl.dom.innerHTML = 'X';
6314                 textEl.appendTo(document.body);
6315                 textSize = textEl.dom.offsetHeight;
6316                 setInterval(function(){
6317                     if(textEl.dom.offsetHeight != textSize){
6318                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6319                     }
6320                 }, this.textResizeInterval);
6321             }
6322             textEvent.addListener(fn, scope, options);
6323         },
6324
6325         /**
6326          * Removes the passed window resize listener.
6327          * @param {Function} fn        The method the event invokes
6328          * @param {Object}   scope    The scope of handler
6329          */
6330         removeResizeListener : function(fn, scope){
6331             if(resizeEvent){
6332                 resizeEvent.removeListener(fn, scope);
6333             }
6334         },
6335
6336         // private
6337         fireResize : function(){
6338             if(resizeEvent){
6339                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6340             }   
6341         },
6342         /**
6343          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6344          */
6345         ieDeferSrc : false,
6346         /**
6347          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6348          */
6349         textResizeInterval : 50
6350     };
6351     
6352     /**
6353      * Fix for doc tools
6354      * @scopeAlias pub=Roo.EventManager
6355      */
6356     
6357      /**
6358      * Appends an event handler to an element (shorthand for addListener)
6359      * @param {String/HTMLElement}   element        The html element or id to assign the
6360      * @param {String}   eventName The type of event to listen for
6361      * @param {Function} handler The method the event invokes
6362      * @param {Object}   scope (optional) The scope in which to execute the handler
6363      * function. The handler function's "this" context.
6364      * @param {Object}   options (optional) An object containing handler configuration
6365      * properties. This may contain any of the following properties:<ul>
6366      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6367      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6368      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6369      * <li>preventDefault {Boolean} True to prevent the default action</li>
6370      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6371      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6372      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6373      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6374      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6375      * by the specified number of milliseconds. If the event fires again within that time, the original
6376      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6377      * </ul><br>
6378      * <p>
6379      * <b>Combining Options</b><br>
6380      * Using the options argument, it is possible to combine different types of listeners:<br>
6381      * <br>
6382      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6383      * Code:<pre><code>
6384 el.on('click', this.onClick, this, {
6385     single: true,
6386     delay: 100,
6387     stopEvent : true,
6388     forumId: 4
6389 });</code></pre>
6390      * <p>
6391      * <b>Attaching multiple handlers in 1 call</b><br>
6392       * The method also allows for a single argument to be passed which is a config object containing properties
6393      * which specify multiple handlers.
6394      * <p>
6395      * Code:<pre><code>
6396 el.on({
6397     'click' : {
6398         fn: this.onClick
6399         scope: this,
6400         delay: 100
6401     },
6402     'mouseover' : {
6403         fn: this.onMouseOver
6404         scope: this
6405     },
6406     'mouseout' : {
6407         fn: this.onMouseOut
6408         scope: this
6409     }
6410 });</code></pre>
6411      * <p>
6412      * Or a shorthand syntax:<br>
6413      * Code:<pre><code>
6414 el.on({
6415     'click' : this.onClick,
6416     'mouseover' : this.onMouseOver,
6417     'mouseout' : this.onMouseOut
6418     scope: this
6419 });</code></pre>
6420      */
6421     pub.on = pub.addListener;
6422     pub.un = pub.removeListener;
6423
6424     pub.stoppedMouseDownEvent = new Roo.util.Event();
6425     return pub;
6426 }();
6427 /**
6428   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6429   * @param {Function} fn        The method the event invokes
6430   * @param {Object}   scope    An  object that becomes the scope of the handler
6431   * @param {boolean}  override If true, the obj passed in becomes
6432   *                             the execution scope of the listener
6433   * @member Roo
6434   * @method onReady
6435  */
6436 Roo.onReady = Roo.EventManager.onDocumentReady;
6437
6438 Roo.onReady(function(){
6439     var bd = Roo.get(document.body);
6440     if(!bd){ return; }
6441
6442     var cls = [
6443             Roo.isIE ? "roo-ie"
6444             : Roo.isGecko ? "roo-gecko"
6445             : Roo.isOpera ? "roo-opera"
6446             : Roo.isSafari ? "roo-safari" : ""];
6447
6448     if(Roo.isMac){
6449         cls.push("roo-mac");
6450     }
6451     if(Roo.isLinux){
6452         cls.push("roo-linux");
6453     }
6454     if(Roo.isBorderBox){
6455         cls.push('roo-border-box');
6456     }
6457     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6458         var p = bd.dom.parentNode;
6459         if(p){
6460             p.className += ' roo-strict';
6461         }
6462     }
6463     bd.addClass(cls.join(' '));
6464 });
6465
6466 /**
6467  * @class Roo.EventObject
6468  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6469  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6470  * Example:
6471  * <pre><code>
6472  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6473     e.preventDefault();
6474     var target = e.getTarget();
6475     ...
6476  }
6477  var myDiv = Roo.get("myDiv");
6478  myDiv.on("click", handleClick);
6479  //or
6480  Roo.EventManager.on("myDiv", 'click', handleClick);
6481  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6482  </code></pre>
6483  * @singleton
6484  */
6485 Roo.EventObject = function(){
6486     
6487     var E = Roo.lib.Event;
6488     
6489     // safari keypress events for special keys return bad keycodes
6490     var safariKeys = {
6491         63234 : 37, // left
6492         63235 : 39, // right
6493         63232 : 38, // up
6494         63233 : 40, // down
6495         63276 : 33, // page up
6496         63277 : 34, // page down
6497         63272 : 46, // delete
6498         63273 : 36, // home
6499         63275 : 35  // end
6500     };
6501
6502     // normalize button clicks
6503     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6504                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6505
6506     Roo.EventObjectImpl = function(e){
6507         if(e){
6508             this.setEvent(e.browserEvent || e);
6509         }
6510     };
6511     Roo.EventObjectImpl.prototype = {
6512         /**
6513          * Used to fix doc tools.
6514          * @scope Roo.EventObject.prototype
6515          */
6516             
6517
6518         
6519         
6520         /** The normal browser event */
6521         browserEvent : null,
6522         /** The button pressed in a mouse event */
6523         button : -1,
6524         /** True if the shift key was down during the event */
6525         shiftKey : false,
6526         /** True if the control key was down during the event */
6527         ctrlKey : false,
6528         /** True if the alt key was down during the event */
6529         altKey : false,
6530
6531         /** Key constant 
6532         * @type Number */
6533         BACKSPACE : 8,
6534         /** Key constant 
6535         * @type Number */
6536         TAB : 9,
6537         /** Key constant 
6538         * @type Number */
6539         RETURN : 13,
6540         /** Key constant 
6541         * @type Number */
6542         ENTER : 13,
6543         /** Key constant 
6544         * @type Number */
6545         SHIFT : 16,
6546         /** Key constant 
6547         * @type Number */
6548         CONTROL : 17,
6549         /** Key constant 
6550         * @type Number */
6551         ESC : 27,
6552         /** Key constant 
6553         * @type Number */
6554         SPACE : 32,
6555         /** Key constant 
6556         * @type Number */
6557         PAGEUP : 33,
6558         /** Key constant 
6559         * @type Number */
6560         PAGEDOWN : 34,
6561         /** Key constant 
6562         * @type Number */
6563         END : 35,
6564         /** Key constant 
6565         * @type Number */
6566         HOME : 36,
6567         /** Key constant 
6568         * @type Number */
6569         LEFT : 37,
6570         /** Key constant 
6571         * @type Number */
6572         UP : 38,
6573         /** Key constant 
6574         * @type Number */
6575         RIGHT : 39,
6576         /** Key constant 
6577         * @type Number */
6578         DOWN : 40,
6579         /** Key constant 
6580         * @type Number */
6581         DELETE : 46,
6582         /** Key constant 
6583         * @type Number */
6584         F5 : 116,
6585
6586            /** @private */
6587         setEvent : function(e){
6588             if(e == this || (e && e.browserEvent)){ // already wrapped
6589                 return e;
6590             }
6591             this.browserEvent = e;
6592             if(e){
6593                 // normalize buttons
6594                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6595                 if(e.type == 'click' && this.button == -1){
6596                     this.button = 0;
6597                 }
6598                 this.type = e.type;
6599                 this.shiftKey = e.shiftKey;
6600                 // mac metaKey behaves like ctrlKey
6601                 this.ctrlKey = e.ctrlKey || e.metaKey;
6602                 this.altKey = e.altKey;
6603                 // in getKey these will be normalized for the mac
6604                 this.keyCode = e.keyCode;
6605                 // keyup warnings on firefox.
6606                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6607                 // cache the target for the delayed and or buffered events
6608                 this.target = E.getTarget(e);
6609                 // same for XY
6610                 this.xy = E.getXY(e);
6611             }else{
6612                 this.button = -1;
6613                 this.shiftKey = false;
6614                 this.ctrlKey = false;
6615                 this.altKey = false;
6616                 this.keyCode = 0;
6617                 this.charCode =0;
6618                 this.target = null;
6619                 this.xy = [0, 0];
6620             }
6621             return this;
6622         },
6623
6624         /**
6625          * Stop the event (preventDefault and stopPropagation)
6626          */
6627         stopEvent : function(){
6628             if(this.browserEvent){
6629                 if(this.browserEvent.type == 'mousedown'){
6630                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6631                 }
6632                 E.stopEvent(this.browserEvent);
6633             }
6634         },
6635
6636         /**
6637          * Prevents the browsers default handling of the event.
6638          */
6639         preventDefault : function(){
6640             if(this.browserEvent){
6641                 E.preventDefault(this.browserEvent);
6642             }
6643         },
6644
6645         /** @private */
6646         isNavKeyPress : function(){
6647             var k = this.keyCode;
6648             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6649             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6650         },
6651
6652         isSpecialKey : function(){
6653             var k = this.keyCode;
6654             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6655             (k == 16) || (k == 17) ||
6656             (k >= 18 && k <= 20) ||
6657             (k >= 33 && k <= 35) ||
6658             (k >= 36 && k <= 39) ||
6659             (k >= 44 && k <= 45);
6660         },
6661         /**
6662          * Cancels bubbling of the event.
6663          */
6664         stopPropagation : function(){
6665             if(this.browserEvent){
6666                 if(this.type == 'mousedown'){
6667                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6668                 }
6669                 E.stopPropagation(this.browserEvent);
6670             }
6671         },
6672
6673         /**
6674          * Gets the key code for the event.
6675          * @return {Number}
6676          */
6677         getCharCode : function(){
6678             return this.charCode || this.keyCode;
6679         },
6680
6681         /**
6682          * Returns a normalized keyCode for the event.
6683          * @return {Number} The key code
6684          */
6685         getKey : function(){
6686             var k = this.keyCode || this.charCode;
6687             return Roo.isSafari ? (safariKeys[k] || k) : k;
6688         },
6689
6690         /**
6691          * Gets the x coordinate of the event.
6692          * @return {Number}
6693          */
6694         getPageX : function(){
6695             return this.xy[0];
6696         },
6697
6698         /**
6699          * Gets the y coordinate of the event.
6700          * @return {Number}
6701          */
6702         getPageY : function(){
6703             return this.xy[1];
6704         },
6705
6706         /**
6707          * Gets the time of the event.
6708          * @return {Number}
6709          */
6710         getTime : function(){
6711             if(this.browserEvent){
6712                 return E.getTime(this.browserEvent);
6713             }
6714             return null;
6715         },
6716
6717         /**
6718          * Gets the page coordinates of the event.
6719          * @return {Array} The xy values like [x, y]
6720          */
6721         getXY : function(){
6722             return this.xy;
6723         },
6724
6725         /**
6726          * Gets the target for the event.
6727          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6728          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6729                 search as a number or element (defaults to 10 || document.body)
6730          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6731          * @return {HTMLelement}
6732          */
6733         getTarget : function(selector, maxDepth, returnEl){
6734             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6735         },
6736         /**
6737          * Gets the related target.
6738          * @return {HTMLElement}
6739          */
6740         getRelatedTarget : function(){
6741             if(this.browserEvent){
6742                 return E.getRelatedTarget(this.browserEvent);
6743             }
6744             return null;
6745         },
6746
6747         /**
6748          * Normalizes mouse wheel delta across browsers
6749          * @return {Number} The delta
6750          */
6751         getWheelDelta : function(){
6752             var e = this.browserEvent;
6753             var delta = 0;
6754             if(e.wheelDelta){ /* IE/Opera. */
6755                 delta = e.wheelDelta/120;
6756             }else if(e.detail){ /* Mozilla case. */
6757                 delta = -e.detail/3;
6758             }
6759             return delta;
6760         },
6761
6762         /**
6763          * Returns true if the control, meta, shift or alt key was pressed during this event.
6764          * @return {Boolean}
6765          */
6766         hasModifier : function(){
6767             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6768         },
6769
6770         /**
6771          * Returns true if the target of this event equals el or is a child of el
6772          * @param {String/HTMLElement/Element} el
6773          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6774          * @return {Boolean}
6775          */
6776         within : function(el, related){
6777             var t = this[related ? "getRelatedTarget" : "getTarget"]();
6778             return t && Roo.fly(el).contains(t);
6779         },
6780
6781         getPoint : function(){
6782             return new Roo.lib.Point(this.xy[0], this.xy[1]);
6783         }
6784     };
6785
6786     return new Roo.EventObjectImpl();
6787 }();
6788             
6789     /*
6790  * Based on:
6791  * Ext JS Library 1.1.1
6792  * Copyright(c) 2006-2007, Ext JS, LLC.
6793  *
6794  * Originally Released Under LGPL - original licence link has changed is not relivant.
6795  *
6796  * Fork - LGPL
6797  * <script type="text/javascript">
6798  */
6799
6800  
6801 // was in Composite Element!??!?!
6802  
6803 (function(){
6804     var D = Roo.lib.Dom;
6805     var E = Roo.lib.Event;
6806     var A = Roo.lib.Anim;
6807
6808     // local style camelizing for speed
6809     var propCache = {};
6810     var camelRe = /(-[a-z])/gi;
6811     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
6812     var view = document.defaultView;
6813
6814 /**
6815  * @class Roo.Element
6816  * Represents an Element in the DOM.<br><br>
6817  * Usage:<br>
6818 <pre><code>
6819 var el = Roo.get("my-div");
6820
6821 // or with getEl
6822 var el = getEl("my-div");
6823
6824 // or with a DOM element
6825 var el = Roo.get(myDivElement);
6826 </code></pre>
6827  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
6828  * each call instead of constructing a new one.<br><br>
6829  * <b>Animations</b><br />
6830  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
6831  * should either be a boolean (true) or an object literal with animation options. The animation options are:
6832 <pre>
6833 Option    Default   Description
6834 --------- --------  ---------------------------------------------
6835 duration  .35       The duration of the animation in seconds
6836 easing    easeOut   The YUI easing method
6837 callback  none      A function to execute when the anim completes
6838 scope     this      The scope (this) of the callback function
6839 </pre>
6840 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
6841 * manipulate the animation. Here's an example:
6842 <pre><code>
6843 var el = Roo.get("my-div");
6844
6845 // no animation
6846 el.setWidth(100);
6847
6848 // default animation
6849 el.setWidth(100, true);
6850
6851 // animation with some options set
6852 el.setWidth(100, {
6853     duration: 1,
6854     callback: this.foo,
6855     scope: this
6856 });
6857
6858 // using the "anim" property to get the Anim object
6859 var opt = {
6860     duration: 1,
6861     callback: this.foo,
6862     scope: this
6863 };
6864 el.setWidth(100, opt);
6865 ...
6866 if(opt.anim.isAnimated()){
6867     opt.anim.stop();
6868 }
6869 </code></pre>
6870 * <b> Composite (Collections of) Elements</b><br />
6871  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
6872  * @constructor Create a new Element directly.
6873  * @param {String/HTMLElement} element
6874  * @param {Boolean} forceNew (optional) By default the constructor checks to see if there is already an instance of this element in the cache and if there is it returns the same instance. This will skip that check (useful for extending this class).
6875  */
6876     Roo.Element = function(element, forceNew){
6877         var dom = typeof element == "string" ?
6878                 document.getElementById(element) : element;
6879         if(!dom){ // invalid id/element
6880             return null;
6881         }
6882         var id = dom.id;
6883         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
6884             return Roo.Element.cache[id];
6885         }
6886
6887         /**
6888          * The DOM element
6889          * @type HTMLElement
6890          */
6891         this.dom = dom;
6892
6893         /**
6894          * The DOM element ID
6895          * @type String
6896          */
6897         this.id = id || Roo.id(dom);
6898     };
6899
6900     var El = Roo.Element;
6901
6902     El.prototype = {
6903         /**
6904          * The element's default display mode  (defaults to "")
6905          * @type String
6906          */
6907         originalDisplay : "",
6908
6909         visibilityMode : 1,
6910         /**
6911          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
6912          * @type String
6913          */
6914         defaultUnit : "px",
6915         /**
6916          * Sets the element's visibility mode. When setVisible() is called it
6917          * will use this to determine whether to set the visibility or the display property.
6918          * @param visMode Element.VISIBILITY or Element.DISPLAY
6919          * @return {Roo.Element} this
6920          */
6921         setVisibilityMode : function(visMode){
6922             this.visibilityMode = visMode;
6923             return this;
6924         },
6925         /**
6926          * Convenience method for setVisibilityMode(Element.DISPLAY)
6927          * @param {String} display (optional) What to set display to when visible
6928          * @return {Roo.Element} this
6929          */
6930         enableDisplayMode : function(display){
6931             this.setVisibilityMode(El.DISPLAY);
6932             if(typeof display != "undefined") this.originalDisplay = display;
6933             return this;
6934         },
6935
6936         /**
6937          * Looks at this node and then at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
6938          * @param {String} selector The simple selector to test
6939          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6940                 search as a number or element (defaults to 10 || document.body)
6941          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6942          * @return {HTMLElement} The matching DOM node (or null if no match was found)
6943          */
6944         findParent : function(simpleSelector, maxDepth, returnEl){
6945             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
6946             maxDepth = maxDepth || 50;
6947             if(typeof maxDepth != "number"){
6948                 stopEl = Roo.getDom(maxDepth);
6949                 maxDepth = 10;
6950             }
6951             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
6952                 if(dq.is(p, simpleSelector)){
6953                     return returnEl ? Roo.get(p) : p;
6954                 }
6955                 depth++;
6956                 p = p.parentNode;
6957             }
6958             return null;
6959         },
6960
6961
6962         /**
6963          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
6964          * @param {String} selector The simple selector to test
6965          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6966                 search as a number or element (defaults to 10 || document.body)
6967          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6968          * @return {HTMLElement} The matching DOM node (or null if no match was found)
6969          */
6970         findParentNode : function(simpleSelector, maxDepth, returnEl){
6971             var p = Roo.fly(this.dom.parentNode, '_internal');
6972             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
6973         },
6974
6975         /**
6976          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
6977          * This is a shortcut for findParentNode() that always returns an Roo.Element.
6978          * @param {String} selector The simple selector to test
6979          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6980                 search as a number or element (defaults to 10 || document.body)
6981          * @return {Roo.Element} The matching DOM node (or null if no match was found)
6982          */
6983         up : function(simpleSelector, maxDepth){
6984             return this.findParentNode(simpleSelector, maxDepth, true);
6985         },
6986
6987
6988
6989         /**
6990          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
6991          * @param {String} selector The simple selector to test
6992          * @return {Boolean} True if this element matches the selector, else false
6993          */
6994         is : function(simpleSelector){
6995             return Roo.DomQuery.is(this.dom, simpleSelector);
6996         },
6997
6998         /**
6999          * Perform animation on this element.
7000          * @param {Object} args The YUI animation control args
7001          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
7002          * @param {Function} onComplete (optional) Function to call when animation completes
7003          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
7004          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
7005          * @return {Roo.Element} this
7006          */
7007         animate : function(args, duration, onComplete, easing, animType){
7008             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7009             return this;
7010         },
7011
7012         /*
7013          * @private Internal animation call
7014          */
7015         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7016             animType = animType || 'run';
7017             opt = opt || {};
7018             var anim = Roo.lib.Anim[animType](
7019                 this.dom, args,
7020                 (opt.duration || defaultDur) || .35,
7021                 (opt.easing || defaultEase) || 'easeOut',
7022                 function(){
7023                     Roo.callback(cb, this);
7024                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7025                 },
7026                 this
7027             );
7028             opt.anim = anim;
7029             return anim;
7030         },
7031
7032         // private legacy anim prep
7033         preanim : function(a, i){
7034             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7035         },
7036
7037         /**
7038          * Removes worthless text nodes
7039          * @param {Boolean} forceReclean (optional) By default the element
7040          * keeps track if it has been cleaned already so
7041          * you can call this over and over. However, if you update the element and
7042          * need to force a reclean, you can pass true.
7043          */
7044         clean : function(forceReclean){
7045             if(this.isCleaned && forceReclean !== true){
7046                 return this;
7047             }
7048             var ns = /\S/;
7049             var d = this.dom, n = d.firstChild, ni = -1;
7050             while(n){
7051                 var nx = n.nextSibling;
7052                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7053                     d.removeChild(n);
7054                 }else{
7055                     n.nodeIndex = ++ni;
7056                 }
7057                 n = nx;
7058             }
7059             this.isCleaned = true;
7060             return this;
7061         },
7062
7063         // private
7064         calcOffsetsTo : function(el){
7065             el = Roo.get(el);
7066             var d = el.dom;
7067             var restorePos = false;
7068             if(el.getStyle('position') == 'static'){
7069                 el.position('relative');
7070                 restorePos = true;
7071             }
7072             var x = 0, y =0;
7073             var op = this.dom;
7074             while(op && op != d && op.tagName != 'HTML'){
7075                 x+= op.offsetLeft;
7076                 y+= op.offsetTop;
7077                 op = op.offsetParent;
7078             }
7079             if(restorePos){
7080                 el.position('static');
7081             }
7082             return [x, y];
7083         },
7084
7085         /**
7086          * Scrolls this element into view within the passed container.
7087          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7088          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7089          * @return {Roo.Element} this
7090          */
7091         scrollIntoView : function(container, hscroll){
7092             var c = Roo.getDom(container) || document.body;
7093             var el = this.dom;
7094
7095             var o = this.calcOffsetsTo(c),
7096                 l = o[0],
7097                 t = o[1],
7098                 b = t+el.offsetHeight,
7099                 r = l+el.offsetWidth;
7100
7101             var ch = c.clientHeight;
7102             var ct = parseInt(c.scrollTop, 10);
7103             var cl = parseInt(c.scrollLeft, 10);
7104             var cb = ct + ch;
7105             var cr = cl + c.clientWidth;
7106
7107             if(t < ct){
7108                 c.scrollTop = t;
7109             }else if(b > cb){
7110                 c.scrollTop = b-ch;
7111             }
7112
7113             if(hscroll !== false){
7114                 if(l < cl){
7115                     c.scrollLeft = l;
7116                 }else if(r > cr){
7117                     c.scrollLeft = r-c.clientWidth;
7118                 }
7119             }
7120             return this;
7121         },
7122
7123         // private
7124         scrollChildIntoView : function(child, hscroll){
7125             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7126         },
7127
7128         /**
7129          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7130          * the new height may not be available immediately.
7131          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7132          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7133          * @param {Function} onComplete (optional) Function to call when animation completes
7134          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7135          * @return {Roo.Element} this
7136          */
7137         autoHeight : function(animate, duration, onComplete, easing){
7138             var oldHeight = this.getHeight();
7139             this.clip();
7140             this.setHeight(1); // force clipping
7141             setTimeout(function(){
7142                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7143                 if(!animate){
7144                     this.setHeight(height);
7145                     this.unclip();
7146                     if(typeof onComplete == "function"){
7147                         onComplete();
7148                     }
7149                 }else{
7150                     this.setHeight(oldHeight); // restore original height
7151                     this.setHeight(height, animate, duration, function(){
7152                         this.unclip();
7153                         if(typeof onComplete == "function") onComplete();
7154                     }.createDelegate(this), easing);
7155                 }
7156             }.createDelegate(this), 0);
7157             return this;
7158         },
7159
7160         /**
7161          * Returns true if this element is an ancestor of the passed element
7162          * @param {HTMLElement/String} el The element to check
7163          * @return {Boolean} True if this element is an ancestor of el, else false
7164          */
7165         contains : function(el){
7166             if(!el){return false;}
7167             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7168         },
7169
7170         /**
7171          * Checks whether the element is currently visible using both visibility and display properties.
7172          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7173          * @return {Boolean} True if the element is currently visible, else false
7174          */
7175         isVisible : function(deep) {
7176             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7177             if(deep !== true || !vis){
7178                 return vis;
7179             }
7180             var p = this.dom.parentNode;
7181             while(p && p.tagName.toLowerCase() != "body"){
7182                 if(!Roo.fly(p, '_isVisible').isVisible()){
7183                     return false;
7184                 }
7185                 p = p.parentNode;
7186             }
7187             return true;
7188         },
7189
7190         /**
7191          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7192          * @param {String} selector The CSS selector
7193          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7194          * @return {CompositeElement/CompositeElementLite} The composite element
7195          */
7196         select : function(selector, unique){
7197             return El.select(selector, unique, this.dom);
7198         },
7199
7200         /**
7201          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7202          * @param {String} selector The CSS selector
7203          * @return {Array} An array of the matched nodes
7204          */
7205         query : function(selector, unique){
7206             return Roo.DomQuery.select(selector, this.dom);
7207         },
7208
7209         /**
7210          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7211          * @param {String} selector The CSS selector
7212          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7213          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7214          */
7215         child : function(selector, returnDom){
7216             var n = Roo.DomQuery.selectNode(selector, this.dom);
7217             return returnDom ? n : Roo.get(n);
7218         },
7219
7220         /**
7221          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7222          * @param {String} selector The CSS selector
7223          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7224          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7225          */
7226         down : function(selector, returnDom){
7227             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7228             return returnDom ? n : Roo.get(n);
7229         },
7230
7231         /**
7232          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7233          * @param {String} group The group the DD object is member of
7234          * @param {Object} config The DD config object
7235          * @param {Object} overrides An object containing methods to override/implement on the DD object
7236          * @return {Roo.dd.DD} The DD object
7237          */
7238         initDD : function(group, config, overrides){
7239             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7240             return Roo.apply(dd, overrides);
7241         },
7242
7243         /**
7244          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7245          * @param {String} group The group the DDProxy object is member of
7246          * @param {Object} config The DDProxy config object
7247          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7248          * @return {Roo.dd.DDProxy} The DDProxy object
7249          */
7250         initDDProxy : function(group, config, overrides){
7251             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7252             return Roo.apply(dd, overrides);
7253         },
7254
7255         /**
7256          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7257          * @param {String} group The group the DDTarget object is member of
7258          * @param {Object} config The DDTarget config object
7259          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7260          * @return {Roo.dd.DDTarget} The DDTarget object
7261          */
7262         initDDTarget : function(group, config, overrides){
7263             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7264             return Roo.apply(dd, overrides);
7265         },
7266
7267         /**
7268          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7269          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7270          * @param {Boolean} visible Whether the element is visible
7271          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7272          * @return {Roo.Element} this
7273          */
7274          setVisible : function(visible, animate){
7275             if(!animate || !A){
7276                 if(this.visibilityMode == El.DISPLAY){
7277                     this.setDisplayed(visible);
7278                 }else{
7279                     this.fixDisplay();
7280                     this.dom.style.visibility = visible ? "visible" : "hidden";
7281                 }
7282             }else{
7283                 // closure for composites
7284                 var dom = this.dom;
7285                 var visMode = this.visibilityMode;
7286                 if(visible){
7287                     this.setOpacity(.01);
7288                     this.setVisible(true);
7289                 }
7290                 this.anim({opacity: { to: (visible?1:0) }},
7291                       this.preanim(arguments, 1),
7292                       null, .35, 'easeIn', function(){
7293                          if(!visible){
7294                              if(visMode == El.DISPLAY){
7295                                  dom.style.display = "none";
7296                              }else{
7297                                  dom.style.visibility = "hidden";
7298                              }
7299                              Roo.get(dom).setOpacity(1);
7300                          }
7301                      });
7302             }
7303             return this;
7304         },
7305
7306         /**
7307          * Returns true if display is not "none"
7308          * @return {Boolean}
7309          */
7310         isDisplayed : function() {
7311             return this.getStyle("display") != "none";
7312         },
7313
7314         /**
7315          * Toggles the element's visibility or display, depending on visibility mode.
7316          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7317          * @return {Roo.Element} this
7318          */
7319         toggle : function(animate){
7320             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7321             return this;
7322         },
7323
7324         /**
7325          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7326          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7327          * @return {Roo.Element} this
7328          */
7329         setDisplayed : function(value) {
7330             if(typeof value == "boolean"){
7331                value = value ? this.originalDisplay : "none";
7332             }
7333             this.setStyle("display", value);
7334             return this;
7335         },
7336
7337         /**
7338          * Tries to focus the element. Any exceptions are caught and ignored.
7339          * @return {Roo.Element} this
7340          */
7341         focus : function() {
7342             try{
7343                 this.dom.focus();
7344             }catch(e){}
7345             return this;
7346         },
7347
7348         /**
7349          * Tries to blur the element. Any exceptions are caught and ignored.
7350          * @return {Roo.Element} this
7351          */
7352         blur : function() {
7353             try{
7354                 this.dom.blur();
7355             }catch(e){}
7356             return this;
7357         },
7358
7359         /**
7360          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7361          * @param {String/Array} className The CSS class to add, or an array of classes
7362          * @return {Roo.Element} this
7363          */
7364         addClass : function(className){
7365             if(className instanceof Array){
7366                 for(var i = 0, len = className.length; i < len; i++) {
7367                     this.addClass(className[i]);
7368                 }
7369             }else{
7370                 if(className && !this.hasClass(className)){
7371                     this.dom.className = this.dom.className + " " + className;
7372                 }
7373             }
7374             return this;
7375         },
7376
7377         /**
7378          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7379          * @param {String/Array} className The CSS class to add, or an array of classes
7380          * @return {Roo.Element} this
7381          */
7382         radioClass : function(className){
7383             var siblings = this.dom.parentNode.childNodes;
7384             for(var i = 0; i < siblings.length; i++) {
7385                 var s = siblings[i];
7386                 if(s.nodeType == 1){
7387                     Roo.get(s).removeClass(className);
7388                 }
7389             }
7390             this.addClass(className);
7391             return this;
7392         },
7393
7394         /**
7395          * Removes one or more CSS classes from the element.
7396          * @param {String/Array} className The CSS class to remove, or an array of classes
7397          * @return {Roo.Element} this
7398          */
7399         removeClass : function(className){
7400             if(!className || !this.dom.className){
7401                 return this;
7402             }
7403             if(className instanceof Array){
7404                 for(var i = 0, len = className.length; i < len; i++) {
7405                     this.removeClass(className[i]);
7406                 }
7407             }else{
7408                 if(this.hasClass(className)){
7409                     var re = this.classReCache[className];
7410                     if (!re) {
7411                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7412                        this.classReCache[className] = re;
7413                     }
7414                     this.dom.className =
7415                         this.dom.className.replace(re, " ");
7416                 }
7417             }
7418             return this;
7419         },
7420
7421         // private
7422         classReCache: {},
7423
7424         /**
7425          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7426          * @param {String} className The CSS class to toggle
7427          * @return {Roo.Element} this
7428          */
7429         toggleClass : function(className){
7430             if(this.hasClass(className)){
7431                 this.removeClass(className);
7432             }else{
7433                 this.addClass(className);
7434             }
7435             return this;
7436         },
7437
7438         /**
7439          * Checks if the specified CSS class exists on this element's DOM node.
7440          * @param {String} className The CSS class to check for
7441          * @return {Boolean} True if the class exists, else false
7442          */
7443         hasClass : function(className){
7444             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7445         },
7446
7447         /**
7448          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7449          * @param {String} oldClassName The CSS class to replace
7450          * @param {String} newClassName The replacement CSS class
7451          * @return {Roo.Element} this
7452          */
7453         replaceClass : function(oldClassName, newClassName){
7454             this.removeClass(oldClassName);
7455             this.addClass(newClassName);
7456             return this;
7457         },
7458
7459         /**
7460          * Returns an object with properties matching the styles requested.
7461          * For example, el.getStyles('color', 'font-size', 'width') might return
7462          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7463          * @param {String} style1 A style name
7464          * @param {String} style2 A style name
7465          * @param {String} etc.
7466          * @return {Object} The style object
7467          */
7468         getStyles : function(){
7469             var a = arguments, len = a.length, r = {};
7470             for(var i = 0; i < len; i++){
7471                 r[a[i]] = this.getStyle(a[i]);
7472             }
7473             return r;
7474         },
7475
7476         /**
7477          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7478          * @param {String} property The style property whose value is returned.
7479          * @return {String} The current value of the style property for this element.
7480          */
7481         getStyle : function(){
7482             return view && view.getComputedStyle ?
7483                 function(prop){
7484                     var el = this.dom, v, cs, camel;
7485                     if(prop == 'float'){
7486                         prop = "cssFloat";
7487                     }
7488                     if(el.style && (v = el.style[prop])){
7489                         return v;
7490                     }
7491                     if(cs = view.getComputedStyle(el, "")){
7492                         if(!(camel = propCache[prop])){
7493                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7494                         }
7495                         return cs[camel];
7496                     }
7497                     return null;
7498                 } :
7499                 function(prop){
7500                     var el = this.dom, v, cs, camel;
7501                     if(prop == 'opacity'){
7502                         if(typeof el.style.filter == 'string'){
7503                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7504                             if(m){
7505                                 var fv = parseFloat(m[1]);
7506                                 if(!isNaN(fv)){
7507                                     return fv ? fv / 100 : 0;
7508                                 }
7509                             }
7510                         }
7511                         return 1;
7512                     }else if(prop == 'float'){
7513                         prop = "styleFloat";
7514                     }
7515                     if(!(camel = propCache[prop])){
7516                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7517                     }
7518                     if(v = el.style[camel]){
7519                         return v;
7520                     }
7521                     if(cs = el.currentStyle){
7522                         return cs[camel];
7523                     }
7524                     return null;
7525                 };
7526         }(),
7527
7528         /**
7529          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7530          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7531          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7532          * @return {Roo.Element} this
7533          */
7534         setStyle : function(prop, value){
7535             if(typeof prop == "string"){
7536                 
7537                 if (prop == 'float') {
7538                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7539                     return this;
7540                 }
7541                 
7542                 var camel;
7543                 if(!(camel = propCache[prop])){
7544                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7545                 }
7546                 
7547                 if(camel == 'opacity') {
7548                     this.setOpacity(value);
7549                 }else{
7550                     this.dom.style[camel] = value;
7551                 }
7552             }else{
7553                 for(var style in prop){
7554                     if(typeof prop[style] != "function"){
7555                        this.setStyle(style, prop[style]);
7556                     }
7557                 }
7558             }
7559             return this;
7560         },
7561
7562         /**
7563          * More flexible version of {@link #setStyle} for setting style properties.
7564          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7565          * a function which returns such a specification.
7566          * @return {Roo.Element} this
7567          */
7568         applyStyles : function(style){
7569             Roo.DomHelper.applyStyles(this.dom, style);
7570             return this;
7571         },
7572
7573         /**
7574           * Gets the current X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7575           * @return {Number} The X position of the element
7576           */
7577         getX : function(){
7578             return D.getX(this.dom);
7579         },
7580
7581         /**
7582           * Gets the current Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7583           * @return {Number} The Y position of the element
7584           */
7585         getY : function(){
7586             return D.getY(this.dom);
7587         },
7588
7589         /**
7590           * Gets the current position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7591           * @return {Array} The XY position of the element
7592           */
7593         getXY : function(){
7594             return D.getXY(this.dom);
7595         },
7596
7597         /**
7598          * Sets the X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7599          * @param {Number} The X position of the element
7600          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7601          * @return {Roo.Element} this
7602          */
7603         setX : function(x, animate){
7604             if(!animate || !A){
7605                 D.setX(this.dom, x);
7606             }else{
7607                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7608             }
7609             return this;
7610         },
7611
7612         /**
7613          * Sets the Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7614          * @param {Number} The Y position of the element
7615          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7616          * @return {Roo.Element} this
7617          */
7618         setY : function(y, animate){
7619             if(!animate || !A){
7620                 D.setY(this.dom, y);
7621             }else{
7622                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7623             }
7624             return this;
7625         },
7626
7627         /**
7628          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7629          * @param {String} left The left CSS property value
7630          * @return {Roo.Element} this
7631          */
7632         setLeft : function(left){
7633             this.setStyle("left", this.addUnits(left));
7634             return this;
7635         },
7636
7637         /**
7638          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7639          * @param {String} top The top CSS property value
7640          * @return {Roo.Element} this
7641          */
7642         setTop : function(top){
7643             this.setStyle("top", this.addUnits(top));
7644             return this;
7645         },
7646
7647         /**
7648          * Sets the element's CSS right style.
7649          * @param {String} right The right CSS property value
7650          * @return {Roo.Element} this
7651          */
7652         setRight : function(right){
7653             this.setStyle("right", this.addUnits(right));
7654             return this;
7655         },
7656
7657         /**
7658          * Sets the element's CSS bottom style.
7659          * @param {String} bottom The bottom CSS property value
7660          * @return {Roo.Element} this
7661          */
7662         setBottom : function(bottom){
7663             this.setStyle("bottom", this.addUnits(bottom));
7664             return this;
7665         },
7666
7667         /**
7668          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7669          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7670          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7671          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7672          * @return {Roo.Element} this
7673          */
7674         setXY : function(pos, animate){
7675             if(!animate || !A){
7676                 D.setXY(this.dom, pos);
7677             }else{
7678                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7679             }
7680             return this;
7681         },
7682
7683         /**
7684          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7685          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7686          * @param {Number} x X value for new position (coordinates are page-based)
7687          * @param {Number} y Y value for new position (coordinates are page-based)
7688          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7689          * @return {Roo.Element} this
7690          */
7691         setLocation : function(x, y, animate){
7692             this.setXY([x, y], this.preanim(arguments, 2));
7693             return this;
7694         },
7695
7696         /**
7697          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7698          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7699          * @param {Number} x X value for new position (coordinates are page-based)
7700          * @param {Number} y Y value for new position (coordinates are page-based)
7701          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7702          * @return {Roo.Element} this
7703          */
7704         moveTo : function(x, y, animate){
7705             this.setXY([x, y], this.preanim(arguments, 2));
7706             return this;
7707         },
7708
7709         /**
7710          * Returns the region of the given element.
7711          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7712          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7713          */
7714         getRegion : function(){
7715             return D.getRegion(this.dom);
7716         },
7717
7718         /**
7719          * Returns the offset height of the element
7720          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7721          * @return {Number} The element's height
7722          */
7723         getHeight : function(contentHeight){
7724             var h = this.dom.offsetHeight || 0;
7725             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7726         },
7727
7728         /**
7729          * Returns the offset width of the element
7730          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7731          * @return {Number} The element's width
7732          */
7733         getWidth : function(contentWidth){
7734             var w = this.dom.offsetWidth || 0;
7735             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7736         },
7737
7738         /**
7739          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7740          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7741          * if a height has not been set using CSS.
7742          * @return {Number}
7743          */
7744         getComputedHeight : function(){
7745             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7746             if(!h){
7747                 h = parseInt(this.getStyle('height'), 10) || 0;
7748                 if(!this.isBorderBox()){
7749                     h += this.getFrameWidth('tb');
7750                 }
7751             }
7752             return h;
7753         },
7754
7755         /**
7756          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7757          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7758          * if a width has not been set using CSS.
7759          * @return {Number}
7760          */
7761         getComputedWidth : function(){
7762             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
7763             if(!w){
7764                 w = parseInt(this.getStyle('width'), 10) || 0;
7765                 if(!this.isBorderBox()){
7766                     w += this.getFrameWidth('lr');
7767                 }
7768             }
7769             return w;
7770         },
7771
7772         /**
7773          * Returns the size of the element.
7774          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
7775          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
7776          */
7777         getSize : function(contentSize){
7778             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
7779         },
7780
7781         /**
7782          * Returns the width and height of the viewport.
7783          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
7784          */
7785         getViewSize : function(){
7786             var d = this.dom, doc = document, aw = 0, ah = 0;
7787             if(d == doc || d == doc.body){
7788                 return {width : D.getViewWidth(), height: D.getViewHeight()};
7789             }else{
7790                 return {
7791                     width : d.clientWidth,
7792                     height: d.clientHeight
7793                 };
7794             }
7795         },
7796
7797         /**
7798          * Returns the value of the "value" attribute
7799          * @param {Boolean} asNumber true to parse the value as a number
7800          * @return {String/Number}
7801          */
7802         getValue : function(asNumber){
7803             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
7804         },
7805
7806         // private
7807         adjustWidth : function(width){
7808             if(typeof width == "number"){
7809                 if(this.autoBoxAdjust && !this.isBorderBox()){
7810                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
7811                 }
7812                 if(width < 0){
7813                     width = 0;
7814                 }
7815             }
7816             return width;
7817         },
7818
7819         // private
7820         adjustHeight : function(height){
7821             if(typeof height == "number"){
7822                if(this.autoBoxAdjust && !this.isBorderBox()){
7823                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
7824                }
7825                if(height < 0){
7826                    height = 0;
7827                }
7828             }
7829             return height;
7830         },
7831
7832         /**
7833          * Set the width of the element
7834          * @param {Number} width The new width
7835          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7836          * @return {Roo.Element} this
7837          */
7838         setWidth : function(width, animate){
7839             width = this.adjustWidth(width);
7840             if(!animate || !A){
7841                 this.dom.style.width = this.addUnits(width);
7842             }else{
7843                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
7844             }
7845             return this;
7846         },
7847
7848         /**
7849          * Set the height of the element
7850          * @param {Number} height The new height
7851          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7852          * @return {Roo.Element} this
7853          */
7854          setHeight : function(height, animate){
7855             height = this.adjustHeight(height);
7856             if(!animate || !A){
7857                 this.dom.style.height = this.addUnits(height);
7858             }else{
7859                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
7860             }
7861             return this;
7862         },
7863
7864         /**
7865          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
7866          * @param {Number} width The new width
7867          * @param {Number} height The new height
7868          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7869          * @return {Roo.Element} this
7870          */
7871          setSize : function(width, height, animate){
7872             if(typeof width == "object"){ // in case of object from getSize()
7873                 height = width.height; width = width.width;
7874             }
7875             width = this.adjustWidth(width); height = this.adjustHeight(height);
7876             if(!animate || !A){
7877                 this.dom.style.width = this.addUnits(width);
7878                 this.dom.style.height = this.addUnits(height);
7879             }else{
7880                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
7881             }
7882             return this;
7883         },
7884
7885         /**
7886          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
7887          * @param {Number} x X value for new position (coordinates are page-based)
7888          * @param {Number} y Y value for new position (coordinates are page-based)
7889          * @param {Number} width The new width
7890          * @param {Number} height The new height
7891          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7892          * @return {Roo.Element} this
7893          */
7894         setBounds : function(x, y, width, height, animate){
7895             if(!animate || !A){
7896                 this.setSize(width, height);
7897                 this.setLocation(x, y);
7898             }else{
7899                 width = this.adjustWidth(width); height = this.adjustHeight(height);
7900                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
7901                               this.preanim(arguments, 4), 'motion');
7902             }
7903             return this;
7904         },
7905
7906         /**
7907          * Sets the element's position and size the the specified region. If animation is true then width, height, x and y will be animated concurrently.
7908          * @param {Roo.lib.Region} region The region to fill
7909          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7910          * @return {Roo.Element} this
7911          */
7912         setRegion : function(region, animate){
7913             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
7914             return this;
7915         },
7916
7917         /**
7918          * Appends an event handler
7919          *
7920          * @param {String}   eventName     The type of event to append
7921          * @param {Function} fn        The method the event invokes
7922          * @param {Object} scope       (optional) The scope (this object) of the fn
7923          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
7924          */
7925         addListener : function(eventName, fn, scope, options){
7926             if (this.dom) {
7927                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
7928             }
7929         },
7930
7931         /**
7932          * Removes an event handler from this element
7933          * @param {String} eventName the type of event to remove
7934          * @param {Function} fn the method the event invokes
7935          * @return {Roo.Element} this
7936          */
7937         removeListener : function(eventName, fn){
7938             Roo.EventManager.removeListener(this.dom,  eventName, fn);
7939             return this;
7940         },
7941
7942         /**
7943          * Removes all previous added listeners from this element
7944          * @return {Roo.Element} this
7945          */
7946         removeAllListeners : function(){
7947             E.purgeElement(this.dom);
7948             return this;
7949         },
7950
7951         relayEvent : function(eventName, observable){
7952             this.on(eventName, function(e){
7953                 observable.fireEvent(eventName, e);
7954             });
7955         },
7956
7957         /**
7958          * Set the opacity of the element
7959          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
7960          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7961          * @return {Roo.Element} this
7962          */
7963          setOpacity : function(opacity, animate){
7964             if(!animate || !A){
7965                 var s = this.dom.style;
7966                 if(Roo.isIE){
7967                     s.zoom = 1;
7968                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
7969                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
7970                 }else{
7971                     s.opacity = opacity;
7972                 }
7973             }else{
7974                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
7975             }
7976             return this;
7977         },
7978
7979         /**
7980          * Gets the left X coordinate
7981          * @param {Boolean} local True to get the local css position instead of page coordinate
7982          * @return {Number}
7983          */
7984         getLeft : function(local){
7985             if(!local){
7986                 return this.getX();
7987             }else{
7988                 return parseInt(this.getStyle("left"), 10) || 0;
7989             }
7990         },
7991
7992         /**
7993          * Gets the right X coordinate of the element (element X position + element width)
7994          * @param {Boolean} local True to get the local css position instead of page coordinate
7995          * @return {Number}
7996          */
7997         getRight : function(local){
7998             if(!local){
7999                 return this.getX() + this.getWidth();
8000             }else{
8001                 return (this.getLeft(true) + this.getWidth()) || 0;
8002             }
8003         },
8004
8005         /**
8006          * Gets the top Y coordinate
8007          * @param {Boolean} local True to get the local css position instead of page coordinate
8008          * @return {Number}
8009          */
8010         getTop : function(local) {
8011             if(!local){
8012                 return this.getY();
8013             }else{
8014                 return parseInt(this.getStyle("top"), 10) || 0;
8015             }
8016         },
8017
8018         /**
8019          * Gets the bottom Y coordinate of the element (element Y position + element height)
8020          * @param {Boolean} local True to get the local css position instead of page coordinate
8021          * @return {Number}
8022          */
8023         getBottom : function(local){
8024             if(!local){
8025                 return this.getY() + this.getHeight();
8026             }else{
8027                 return (this.getTop(true) + this.getHeight()) || 0;
8028             }
8029         },
8030
8031         /**
8032         * Initializes positioning on this element. If a desired position is not passed, it will make the
8033         * the element positioned relative IF it is not already positioned.
8034         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8035         * @param {Number} zIndex (optional) The zIndex to apply
8036         * @param {Number} x (optional) Set the page X position
8037         * @param {Number} y (optional) Set the page Y position
8038         */
8039         position : function(pos, zIndex, x, y){
8040             if(!pos){
8041                if(this.getStyle('position') == 'static'){
8042                    this.setStyle('position', 'relative');
8043                }
8044             }else{
8045                 this.setStyle("position", pos);
8046             }
8047             if(zIndex){
8048                 this.setStyle("z-index", zIndex);
8049             }
8050             if(x !== undefined && y !== undefined){
8051                 this.setXY([x, y]);
8052             }else if(x !== undefined){
8053                 this.setX(x);
8054             }else if(y !== undefined){
8055                 this.setY(y);
8056             }
8057         },
8058
8059         /**
8060         * Clear positioning back to the default when the document was loaded
8061         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8062         * @return {Roo.Element} this
8063          */
8064         clearPositioning : function(value){
8065             value = value ||'';
8066             this.setStyle({
8067                 "left": value,
8068                 "right": value,
8069                 "top": value,
8070                 "bottom": value,
8071                 "z-index": "",
8072                 "position" : "static"
8073             });
8074             return this;
8075         },
8076
8077         /**
8078         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8079         * snapshot before performing an update and then restoring the element.
8080         * @return {Object}
8081         */
8082         getPositioning : function(){
8083             var l = this.getStyle("left");
8084             var t = this.getStyle("top");
8085             return {
8086                 "position" : this.getStyle("position"),
8087                 "left" : l,
8088                 "right" : l ? "" : this.getStyle("right"),
8089                 "top" : t,
8090                 "bottom" : t ? "" : this.getStyle("bottom"),
8091                 "z-index" : this.getStyle("z-index")
8092             };
8093         },
8094
8095         /**
8096          * Gets the width of the border(s) for the specified side(s)
8097          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8098          * passing lr would get the border (l)eft width + the border (r)ight width.
8099          * @return {Number} The width of the sides passed added together
8100          */
8101         getBorderWidth : function(side){
8102             return this.addStyles(side, El.borders);
8103         },
8104
8105         /**
8106          * Gets the width of the padding(s) for the specified side(s)
8107          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8108          * passing lr would get the padding (l)eft + the padding (r)ight.
8109          * @return {Number} The padding of the sides passed added together
8110          */
8111         getPadding : function(side){
8112             return this.addStyles(side, El.paddings);
8113         },
8114
8115         /**
8116         * Set positioning with an object returned by getPositioning().
8117         * @param {Object} posCfg
8118         * @return {Roo.Element} this
8119          */
8120         setPositioning : function(pc){
8121             this.applyStyles(pc);
8122             if(pc.right == "auto"){
8123                 this.dom.style.right = "";
8124             }
8125             if(pc.bottom == "auto"){
8126                 this.dom.style.bottom = "";
8127             }
8128             return this;
8129         },
8130
8131         // private
8132         fixDisplay : function(){
8133             if(this.getStyle("display") == "none"){
8134                 this.setStyle("visibility", "hidden");
8135                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8136                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8137                     this.setStyle("display", "block");
8138                 }
8139             }
8140         },
8141
8142         /**
8143          * Quick set left and top adding default units
8144          * @param {String} left The left CSS property value
8145          * @param {String} top The top CSS property value
8146          * @return {Roo.Element} this
8147          */
8148          setLeftTop : function(left, top){
8149             this.dom.style.left = this.addUnits(left);
8150             this.dom.style.top = this.addUnits(top);
8151             return this;
8152         },
8153
8154         /**
8155          * Move this element relative to its current position.
8156          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8157          * @param {Number} distance How far to move the element in pixels
8158          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8159          * @return {Roo.Element} this
8160          */
8161          move : function(direction, distance, animate){
8162             var xy = this.getXY();
8163             direction = direction.toLowerCase();
8164             switch(direction){
8165                 case "l":
8166                 case "left":
8167                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8168                     break;
8169                case "r":
8170                case "right":
8171                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8172                     break;
8173                case "t":
8174                case "top":
8175                case "up":
8176                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8177                     break;
8178                case "b":
8179                case "bottom":
8180                case "down":
8181                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8182                     break;
8183             }
8184             return this;
8185         },
8186
8187         /**
8188          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8189          * @return {Roo.Element} this
8190          */
8191         clip : function(){
8192             if(!this.isClipped){
8193                this.isClipped = true;
8194                this.originalClip = {
8195                    "o": this.getStyle("overflow"),
8196                    "x": this.getStyle("overflow-x"),
8197                    "y": this.getStyle("overflow-y")
8198                };
8199                this.setStyle("overflow", "hidden");
8200                this.setStyle("overflow-x", "hidden");
8201                this.setStyle("overflow-y", "hidden");
8202             }
8203             return this;
8204         },
8205
8206         /**
8207          *  Return clipping (overflow) to original clipping before clip() was called
8208          * @return {Roo.Element} this
8209          */
8210         unclip : function(){
8211             if(this.isClipped){
8212                 this.isClipped = false;
8213                 var o = this.originalClip;
8214                 if(o.o){this.setStyle("overflow", o.o);}
8215                 if(o.x){this.setStyle("overflow-x", o.x);}
8216                 if(o.y){this.setStyle("overflow-y", o.y);}
8217             }
8218             return this;
8219         },
8220
8221
8222         /**
8223          * Gets the x,y coordinates specified by the anchor position on the element.
8224          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8225          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8226          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8227          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8228          * @return {Array} [x, y] An array containing the element's x and y coordinates
8229          */
8230         getAnchorXY : function(anchor, local, s){
8231             //Passing a different size is useful for pre-calculating anchors,
8232             //especially for anchored animations that change the el size.
8233
8234             var w, h, vp = false;
8235             if(!s){
8236                 var d = this.dom;
8237                 if(d == document.body || d == document){
8238                     vp = true;
8239                     w = D.getViewWidth(); h = D.getViewHeight();
8240                 }else{
8241                     w = this.getWidth(); h = this.getHeight();
8242                 }
8243             }else{
8244                 w = s.width;  h = s.height;
8245             }
8246             var x = 0, y = 0, r = Math.round;
8247             switch((anchor || "tl").toLowerCase()){
8248                 case "c":
8249                     x = r(w*.5);
8250                     y = r(h*.5);
8251                 break;
8252                 case "t":
8253                     x = r(w*.5);
8254                     y = 0;
8255                 break;
8256                 case "l":
8257                     x = 0;
8258                     y = r(h*.5);
8259                 break;
8260                 case "r":
8261                     x = w;
8262                     y = r(h*.5);
8263                 break;
8264                 case "b":
8265                     x = r(w*.5);
8266                     y = h;
8267                 break;
8268                 case "tl":
8269                     x = 0;
8270                     y = 0;
8271                 break;
8272                 case "bl":
8273                     x = 0;
8274                     y = h;
8275                 break;
8276                 case "br":
8277                     x = w;
8278                     y = h;
8279                 break;
8280                 case "tr":
8281                     x = w;
8282                     y = 0;
8283                 break;
8284             }
8285             if(local === true){
8286                 return [x, y];
8287             }
8288             if(vp){
8289                 var sc = this.getScroll();
8290                 return [x + sc.left, y + sc.top];
8291             }
8292             //Add the element's offset xy
8293             var o = this.getXY();
8294             return [x+o[0], y+o[1]];
8295         },
8296
8297         /**
8298          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8299          * supported position values.
8300          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8301          * @param {String} position The position to align to.
8302          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8303          * @return {Array} [x, y]
8304          */
8305         getAlignToXY : function(el, p, o){
8306             el = Roo.get(el);
8307             var d = this.dom;
8308             if(!el.dom){
8309                 throw "Element.alignTo with an element that doesn't exist";
8310             }
8311             var c = false; //constrain to viewport
8312             var p1 = "", p2 = "";
8313             o = o || [0,0];
8314
8315             if(!p){
8316                 p = "tl-bl";
8317             }else if(p == "?"){
8318                 p = "tl-bl?";
8319             }else if(p.indexOf("-") == -1){
8320                 p = "tl-" + p;
8321             }
8322             p = p.toLowerCase();
8323             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8324             if(!m){
8325                throw "Element.alignTo with an invalid alignment " + p;
8326             }
8327             p1 = m[1]; p2 = m[2]; c = !!m[3];
8328
8329             //Subtract the aligned el's internal xy from the target's offset xy
8330             //plus custom offset to get the aligned el's new offset xy
8331             var a1 = this.getAnchorXY(p1, true);
8332             var a2 = el.getAnchorXY(p2, false);
8333             var x = a2[0] - a1[0] + o[0];
8334             var y = a2[1] - a1[1] + o[1];
8335             if(c){
8336                 //constrain the aligned el to viewport if necessary
8337                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8338                 // 5px of margin for ie
8339                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8340
8341                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8342                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8343                 //otherwise swap the aligned el to the opposite border of the target.
8344                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8345                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8346                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8347                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8348
8349                var doc = document;
8350                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8351                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8352
8353                if((x+w) > dw + scrollX){
8354                     x = swapX ? r.left-w : dw+scrollX-w;
8355                 }
8356                if(x < scrollX){
8357                    x = swapX ? r.right : scrollX;
8358                }
8359                if((y+h) > dh + scrollY){
8360                     y = swapY ? r.top-h : dh+scrollY-h;
8361                 }
8362                if (y < scrollY){
8363                    y = swapY ? r.bottom : scrollY;
8364                }
8365             }
8366             return [x,y];
8367         },
8368
8369         // private
8370         getConstrainToXY : function(){
8371             var os = {top:0, left:0, bottom:0, right: 0};
8372
8373             return function(el, local, offsets, proposedXY){
8374                 el = Roo.get(el);
8375                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8376
8377                 var vw, vh, vx = 0, vy = 0;
8378                 if(el.dom == document.body || el.dom == document){
8379                     vw = Roo.lib.Dom.getViewWidth();
8380                     vh = Roo.lib.Dom.getViewHeight();
8381                 }else{
8382                     vw = el.dom.clientWidth;
8383                     vh = el.dom.clientHeight;
8384                     if(!local){
8385                         var vxy = el.getXY();
8386                         vx = vxy[0];
8387                         vy = vxy[1];
8388                     }
8389                 }
8390
8391                 var s = el.getScroll();
8392
8393                 vx += offsets.left + s.left;
8394                 vy += offsets.top + s.top;
8395
8396                 vw -= offsets.right;
8397                 vh -= offsets.bottom;
8398
8399                 var vr = vx+vw;
8400                 var vb = vy+vh;
8401
8402                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8403                 var x = xy[0], y = xy[1];
8404                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8405
8406                 // only move it if it needs it
8407                 var moved = false;
8408
8409                 // first validate right/bottom
8410                 if((x + w) > vr){
8411                     x = vr - w;
8412                     moved = true;
8413                 }
8414                 if((y + h) > vb){
8415                     y = vb - h;
8416                     moved = true;
8417                 }
8418                 // then make sure top/left isn't negative
8419                 if(x < vx){
8420                     x = vx;
8421                     moved = true;
8422                 }
8423                 if(y < vy){
8424                     y = vy;
8425                     moved = true;
8426                 }
8427                 return moved ? [x, y] : false;
8428             };
8429         }(),
8430
8431         // private
8432         adjustForConstraints : function(xy, parent, offsets){
8433             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8434         },
8435
8436         /**
8437          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8438          * document it aligns it to the viewport.
8439          * The position parameter is optional, and can be specified in any one of the following formats:
8440          * <ul>
8441          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8442          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8443          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8444          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8445          *   <li><b>Two anchors</b>: If two values from the table below are passed separated by a dash, the first value is used as the
8446          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8447          * </ul>
8448          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8449          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8450          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8451          * that specified in order to enforce the viewport constraints.
8452          * Following are all of the supported anchor positions:
8453     <pre>
8454     Value  Description
8455     -----  -----------------------------
8456     tl     The top left corner (default)
8457     t      The center of the top edge
8458     tr     The top right corner
8459     l      The center of the left edge
8460     c      In the center of the element
8461     r      The center of the right edge
8462     bl     The bottom left corner
8463     b      The center of the bottom edge
8464     br     The bottom right corner
8465     </pre>
8466     Example Usage:
8467     <pre><code>
8468     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8469     el.alignTo("other-el");
8470
8471     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8472     el.alignTo("other-el", "tr?");
8473
8474     // align the bottom right corner of el with the center left edge of other-el
8475     el.alignTo("other-el", "br-l?");
8476
8477     // align the center of el with the bottom left corner of other-el and
8478     // adjust the x position by -6 pixels (and the y position by 0)
8479     el.alignTo("other-el", "c-bl", [-6, 0]);
8480     </code></pre>
8481          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8482          * @param {String} position The position to align to.
8483          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8484          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8485          * @return {Roo.Element} this
8486          */
8487         alignTo : function(element, position, offsets, animate){
8488             var xy = this.getAlignToXY(element, position, offsets);
8489             this.setXY(xy, this.preanim(arguments, 3));
8490             return this;
8491         },
8492
8493         /**
8494          * Anchors an element to another element and realigns it when the window is resized.
8495          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8496          * @param {String} position The position to align to.
8497          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8498          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8499          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8500          * is a number, it is used as the buffer delay (defaults to 50ms).
8501          * @param {Function} callback The function to call after the animation finishes
8502          * @return {Roo.Element} this
8503          */
8504         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8505             var action = function(){
8506                 this.alignTo(el, alignment, offsets, animate);
8507                 Roo.callback(callback, this);
8508             };
8509             Roo.EventManager.onWindowResize(action, this);
8510             var tm = typeof monitorScroll;
8511             if(tm != 'undefined'){
8512                 Roo.EventManager.on(window, 'scroll', action, this,
8513                     {buffer: tm == 'number' ? monitorScroll : 50});
8514             }
8515             action.call(this); // align immediately
8516             return this;
8517         },
8518         /**
8519          * Clears any opacity settings from this element. Required in some cases for IE.
8520          * @return {Roo.Element} this
8521          */
8522         clearOpacity : function(){
8523             if (window.ActiveXObject) {
8524                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8525                     this.dom.style.filter = "";
8526                 }
8527             } else {
8528                 this.dom.style.opacity = "";
8529                 this.dom.style["-moz-opacity"] = "";
8530                 this.dom.style["-khtml-opacity"] = "";
8531             }
8532             return this;
8533         },
8534
8535         /**
8536          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8537          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8538          * @return {Roo.Element} this
8539          */
8540         hide : function(animate){
8541             this.setVisible(false, this.preanim(arguments, 0));
8542             return this;
8543         },
8544
8545         /**
8546         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8547         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8548          * @return {Roo.Element} this
8549          */
8550         show : function(animate){
8551             this.setVisible(true, this.preanim(arguments, 0));
8552             return this;
8553         },
8554
8555         /**
8556          * @private Test if size has a unit, otherwise appends the default
8557          */
8558         addUnits : function(size){
8559             return Roo.Element.addUnits(size, this.defaultUnit);
8560         },
8561
8562         /**
8563          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8564          * @return {Roo.Element} this
8565          */
8566         beginMeasure : function(){
8567             var el = this.dom;
8568             if(el.offsetWidth || el.offsetHeight){
8569                 return this; // offsets work already
8570             }
8571             var changed = [];
8572             var p = this.dom, b = document.body; // start with this element
8573             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8574                 var pe = Roo.get(p);
8575                 if(pe.getStyle('display') == 'none'){
8576                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8577                     p.style.visibility = "hidden";
8578                     p.style.display = "block";
8579                 }
8580                 p = p.parentNode;
8581             }
8582             this._measureChanged = changed;
8583             return this;
8584
8585         },
8586
8587         /**
8588          * Restores displays to before beginMeasure was called
8589          * @return {Roo.Element} this
8590          */
8591         endMeasure : function(){
8592             var changed = this._measureChanged;
8593             if(changed){
8594                 for(var i = 0, len = changed.length; i < len; i++) {
8595                     var r = changed[i];
8596                     r.el.style.visibility = r.visibility;
8597                     r.el.style.display = "none";
8598                 }
8599                 this._measureChanged = null;
8600             }
8601             return this;
8602         },
8603
8604         /**
8605         * Update the innerHTML of this element, optionally searching for and processing scripts
8606         * @param {String} html The new HTML
8607         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8608         * @param {Function} callback For async script loading you can be noticed when the update completes
8609         * @return {Roo.Element} this
8610          */
8611         update : function(html, loadScripts, callback){
8612             if(typeof html == "undefined"){
8613                 html = "";
8614             }
8615             if(loadScripts !== true){
8616                 this.dom.innerHTML = html;
8617                 if(typeof callback == "function"){
8618                     callback();
8619                 }
8620                 return this;
8621             }
8622             var id = Roo.id();
8623             var dom = this.dom;
8624
8625             html += '<span id="' + id + '"></span>';
8626
8627             E.onAvailable(id, function(){
8628                 var hd = document.getElementsByTagName("head")[0];
8629                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8630                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8631                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8632
8633                 var match;
8634                 while(match = re.exec(html)){
8635                     var attrs = match[1];
8636                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8637                     if(srcMatch && srcMatch[2]){
8638                        var s = document.createElement("script");
8639                        s.src = srcMatch[2];
8640                        var typeMatch = attrs.match(typeRe);
8641                        if(typeMatch && typeMatch[2]){
8642                            s.type = typeMatch[2];
8643                        }
8644                        hd.appendChild(s);
8645                     }else if(match[2] && match[2].length > 0){
8646                         if(window.execScript) {
8647                            window.execScript(match[2]);
8648                         } else {
8649                             /**
8650                              * eval:var:id
8651                              * eval:var:dom
8652                              * eval:var:html
8653                              * 
8654                              */
8655                            window.eval(match[2]);
8656                         }
8657                     }
8658                 }
8659                 var el = document.getElementById(id);
8660                 if(el){el.parentNode.removeChild(el);}
8661                 if(typeof callback == "function"){
8662                     callback();
8663                 }
8664             });
8665             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8666             return this;
8667         },
8668
8669         /**
8670          * Direct access to the UpdateManager update() method (takes the same parameters).
8671          * @param {String/Function} url The url for this request or a function to call to get the url
8672          * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
8673          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8674          * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used url. If true, it will not store the url.
8675          * @return {Roo.Element} this
8676          */
8677         load : function(){
8678             var um = this.getUpdateManager();
8679             um.update.apply(um, arguments);
8680             return this;
8681         },
8682
8683         /**
8684         * Gets this element's UpdateManager
8685         * @return {Roo.UpdateManager} The UpdateManager
8686         */
8687         getUpdateManager : function(){
8688             if(!this.updateManager){
8689                 this.updateManager = new Roo.UpdateManager(this);
8690             }
8691             return this.updateManager;
8692         },
8693
8694         /**
8695          * Disables text selection for this element (normalized across browsers)
8696          * @return {Roo.Element} this
8697          */
8698         unselectable : function(){
8699             this.dom.unselectable = "on";
8700             this.swallowEvent("selectstart", true);
8701             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8702             this.addClass("x-unselectable");
8703             return this;
8704         },
8705
8706         /**
8707         * Calculates the x, y to center this element on the screen
8708         * @return {Array} The x, y values [x, y]
8709         */
8710         getCenterXY : function(){
8711             return this.getAlignToXY(document, 'c-c');
8712         },
8713
8714         /**
8715         * Centers the Element in either the viewport, or another Element.
8716         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8717         */
8718         center : function(centerIn){
8719             this.alignTo(centerIn || document, 'c-c');
8720             return this;
8721         },
8722
8723         /**
8724          * Tests various css rules/browsers to determine if this element uses a border box
8725          * @return {Boolean}
8726          */
8727         isBorderBox : function(){
8728             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8729         },
8730
8731         /**
8732          * Return a box {x, y, width, height} that can be used to set another elements
8733          * size/location to match this element.
8734          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8735          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8736          * @return {Object} box An object in the format {x, y, width, height}
8737          */
8738         getBox : function(contentBox, local){
8739             var xy;
8740             if(!local){
8741                 xy = this.getXY();
8742             }else{
8743                 var left = parseInt(this.getStyle("left"), 10) || 0;
8744                 var top = parseInt(this.getStyle("top"), 10) || 0;
8745                 xy = [left, top];
8746             }
8747             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8748             if(!contentBox){
8749                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8750             }else{
8751                 var l = this.getBorderWidth("l")+this.getPadding("l");
8752                 var r = this.getBorderWidth("r")+this.getPadding("r");
8753                 var t = this.getBorderWidth("t")+this.getPadding("t");
8754                 var b = this.getBorderWidth("b")+this.getPadding("b");
8755                 bx = {x: xy[0]+l, y: xy[1]+t, 0: xy[0]+l, 1: xy[1]+t, width: w-(l+r), height: h-(t+b)};
8756             }
8757             bx.right = bx.x + bx.width;
8758             bx.bottom = bx.y + bx.height;
8759             return bx;
8760         },
8761
8762         /**
8763          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
8764          for more information about the sides.
8765          * @param {String} sides
8766          * @return {Number}
8767          */
8768         getFrameWidth : function(sides, onlyContentBox){
8769             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
8770         },
8771
8772         /**
8773          * Sets the element's box. Use getBox() on another element to get a box obj. If animate is true then width, height, x and y will be animated concurrently.
8774          * @param {Object} box The box to fill {x, y, width, height}
8775          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
8776          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8777          * @return {Roo.Element} this
8778          */
8779         setBox : function(box, adjust, animate){
8780             var w = box.width, h = box.height;
8781             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
8782                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8783                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8784             }
8785             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
8786             return this;
8787         },
8788
8789         /**
8790          * Forces the browser to repaint this element
8791          * @return {Roo.Element} this
8792          */
8793          repaint : function(){
8794             var dom = this.dom;
8795             this.addClass("x-repaint");
8796             setTimeout(function(){
8797                 Roo.get(dom).removeClass("x-repaint");
8798             }, 1);
8799             return this;
8800         },
8801
8802         /**
8803          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
8804          * then it returns the calculated width of the sides (see getPadding)
8805          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
8806          * @return {Object/Number}
8807          */
8808         getMargins : function(side){
8809             if(!side){
8810                 return {
8811                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
8812                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
8813                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
8814                     right: parseInt(this.getStyle("margin-right"), 10) || 0
8815                 };
8816             }else{
8817                 return this.addStyles(side, El.margins);
8818              }
8819         },
8820
8821         // private
8822         addStyles : function(sides, styles){
8823             var val = 0, v, w;
8824             for(var i = 0, len = sides.length; i < len; i++){
8825                 v = this.getStyle(styles[sides.charAt(i)]);
8826                 if(v){
8827                      w = parseInt(v, 10);
8828                      if(w){ val += w; }
8829                 }
8830             }
8831             return val;
8832         },
8833
8834         /**
8835          * Creates a proxy element of this element
8836          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
8837          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
8838          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
8839          * @return {Roo.Element} The new proxy element
8840          */
8841         createProxy : function(config, renderTo, matchBox){
8842             if(renderTo){
8843                 renderTo = Roo.getDom(renderTo);
8844             }else{
8845                 renderTo = document.body;
8846             }
8847             config = typeof config == "object" ?
8848                 config : {tag : "div", cls: config};
8849             var proxy = Roo.DomHelper.append(renderTo, config, true);
8850             if(matchBox){
8851                proxy.setBox(this.getBox());
8852             }
8853             return proxy;
8854         },
8855
8856         /**
8857          * Puts a mask over this element to disable user interaction. Requires core.css.
8858          * This method can only be applied to elements which accept child nodes.
8859          * @param {String} msg (optional) A message to display in the mask
8860          * @param {String} msgCls (optional) A css class to apply to the msg element
8861          * @return {Element} The mask  element
8862          */
8863         mask : function(msg, msgCls){
8864             if(this.getStyle("position") == "static"){
8865                 this.setStyle("position", "relative");
8866             }
8867             if(!this._mask){
8868                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
8869             }
8870             this.addClass("x-masked");
8871             this._mask.setDisplayed(true);
8872             if(typeof msg == 'string'){
8873                 if(!this._maskMsg){
8874                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
8875                 }
8876                 var mm = this._maskMsg;
8877                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
8878                 mm.dom.firstChild.innerHTML = msg;
8879                 mm.setDisplayed(true);
8880                 mm.center(this);
8881             }
8882             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
8883                 this._mask.setHeight(this.getHeight());
8884             }
8885             return this._mask;
8886         },
8887
8888         /**
8889          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
8890          * it is cached for reuse.
8891          */
8892         unmask : function(removeEl){
8893             if(this._mask){
8894                 if(removeEl === true){
8895                     this._mask.remove();
8896                     delete this._mask;
8897                     if(this._maskMsg){
8898                         this._maskMsg.remove();
8899                         delete this._maskMsg;
8900                     }
8901                 }else{
8902                     this._mask.setDisplayed(false);
8903                     if(this._maskMsg){
8904                         this._maskMsg.setDisplayed(false);
8905                     }
8906                 }
8907             }
8908             this.removeClass("x-masked");
8909         },
8910
8911         /**
8912          * Returns true if this element is masked
8913          * @return {Boolean}
8914          */
8915         isMasked : function(){
8916             return this._mask && this._mask.isVisible();
8917         },
8918
8919         /**
8920          * Creates an iframe shim for this element to keep selects and other windowed objects from
8921          * showing through.
8922          * @return {Roo.Element} The new shim element
8923          */
8924         createShim : function(){
8925             var el = document.createElement('iframe');
8926             el.frameBorder = 'no';
8927             el.className = 'roo-shim';
8928             if(Roo.isIE && Roo.isSecure){
8929                 el.src = Roo.SSL_SECURE_URL;
8930             }
8931             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
8932             shim.autoBoxAdjust = false;
8933             return shim;
8934         },
8935
8936         /**
8937          * Removes this element from the DOM and deletes it from the cache
8938          */
8939         remove : function(){
8940             if(this.dom.parentNode){
8941                 this.dom.parentNode.removeChild(this.dom);
8942             }
8943             delete El.cache[this.dom.id];
8944         },
8945
8946         /**
8947          * Sets up event handlers to add and remove a css class when the mouse is over this element
8948          * @param {String} className
8949          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
8950          * mouseout events for children elements
8951          * @return {Roo.Element} this
8952          */
8953         addClassOnOver : function(className, preventFlicker){
8954             this.on("mouseover", function(){
8955                 Roo.fly(this, '_internal').addClass(className);
8956             }, this.dom);
8957             var removeFn = function(e){
8958                 if(preventFlicker !== true || !e.within(this, true)){
8959                     Roo.fly(this, '_internal').removeClass(className);
8960                 }
8961             };
8962             this.on("mouseout", removeFn, this.dom);
8963             return this;
8964         },
8965
8966         /**
8967          * Sets up event handlers to add and remove a css class when this element has the focus
8968          * @param {String} className
8969          * @return {Roo.Element} this
8970          */
8971         addClassOnFocus : function(className){
8972             this.on("focus", function(){
8973                 Roo.fly(this, '_internal').addClass(className);
8974             }, this.dom);
8975             this.on("blur", function(){
8976                 Roo.fly(this, '_internal').removeClass(className);
8977             }, this.dom);
8978             return this;
8979         },
8980         /**
8981          * 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)
8982          * @param {String} className
8983          * @return {Roo.Element} this
8984          */
8985         addClassOnClick : function(className){
8986             var dom = this.dom;
8987             this.on("mousedown", function(){
8988                 Roo.fly(dom, '_internal').addClass(className);
8989                 var d = Roo.get(document);
8990                 var fn = function(){
8991                     Roo.fly(dom, '_internal').removeClass(className);
8992                     d.removeListener("mouseup", fn);
8993                 };
8994                 d.on("mouseup", fn);
8995             });
8996             return this;
8997         },
8998
8999         /**
9000          * Stops the specified event from bubbling and optionally prevents the default action
9001          * @param {String} eventName
9002          * @param {Boolean} preventDefault (optional) true to prevent the default action too
9003          * @return {Roo.Element} this
9004          */
9005         swallowEvent : function(eventName, preventDefault){
9006             var fn = function(e){
9007                 e.stopPropagation();
9008                 if(preventDefault){
9009                     e.preventDefault();
9010                 }
9011             };
9012             if(eventName instanceof Array){
9013                 for(var i = 0, len = eventName.length; i < len; i++){
9014                      this.on(eventName[i], fn);
9015                 }
9016                 return this;
9017             }
9018             this.on(eventName, fn);
9019             return this;
9020         },
9021
9022         /**
9023          * @private
9024          */
9025       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9026
9027         /**
9028          * Sizes this element to its parent element's dimensions performing
9029          * neccessary box adjustments.
9030          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9031          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9032          * @return {Roo.Element} this
9033          */
9034         fitToParent : function(monitorResize, targetParent) {
9035           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9036           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9037           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9038             return;
9039           }
9040           var p = Roo.get(targetParent || this.dom.parentNode);
9041           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9042           if (monitorResize === true) {
9043             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9044             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9045           }
9046           return this;
9047         },
9048
9049         /**
9050          * Gets the next sibling, skipping text nodes
9051          * @return {HTMLElement} The next sibling or null
9052          */
9053         getNextSibling : function(){
9054             var n = this.dom.nextSibling;
9055             while(n && n.nodeType != 1){
9056                 n = n.nextSibling;
9057             }
9058             return n;
9059         },
9060
9061         /**
9062          * Gets the previous sibling, skipping text nodes
9063          * @return {HTMLElement} The previous sibling or null
9064          */
9065         getPrevSibling : function(){
9066             var n = this.dom.previousSibling;
9067             while(n && n.nodeType != 1){
9068                 n = n.previousSibling;
9069             }
9070             return n;
9071         },
9072
9073
9074         /**
9075          * Appends the passed element(s) to this element
9076          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9077          * @return {Roo.Element} this
9078          */
9079         appendChild: function(el){
9080             el = Roo.get(el);
9081             el.appendTo(this);
9082             return this;
9083         },
9084
9085         /**
9086          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9087          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9088          * automatically generated with the specified attributes.
9089          * @param {HTMLElement} insertBefore (optional) a child element of this element
9090          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9091          * @return {Roo.Element} The new child element
9092          */
9093         createChild: function(config, insertBefore, returnDom){
9094             config = config || {tag:'div'};
9095             if(insertBefore){
9096                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9097             }
9098             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9099         },
9100
9101         /**
9102          * Appends this element to the passed element
9103          * @param {String/HTMLElement/Element} el The new parent element
9104          * @return {Roo.Element} this
9105          */
9106         appendTo: function(el){
9107             el = Roo.getDom(el);
9108             el.appendChild(this.dom);
9109             return this;
9110         },
9111
9112         /**
9113          * Inserts this element before the passed element in the DOM
9114          * @param {String/HTMLElement/Element} el The element to insert before
9115          * @return {Roo.Element} this
9116          */
9117         insertBefore: function(el){
9118             el = Roo.getDom(el);
9119             el.parentNode.insertBefore(this.dom, el);
9120             return this;
9121         },
9122
9123         /**
9124          * Inserts this element after the passed element in the DOM
9125          * @param {String/HTMLElement/Element} el The element to insert after
9126          * @return {Roo.Element} this
9127          */
9128         insertAfter: function(el){
9129             el = Roo.getDom(el);
9130             el.parentNode.insertBefore(this.dom, el.nextSibling);
9131             return this;
9132         },
9133
9134         /**
9135          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9136          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9137          * @return {Roo.Element} The new child
9138          */
9139         insertFirst: function(el, returnDom){
9140             el = el || {};
9141             if(typeof el == 'object' && !el.nodeType){ // dh config
9142                 return this.createChild(el, this.dom.firstChild, returnDom);
9143             }else{
9144                 el = Roo.getDom(el);
9145                 this.dom.insertBefore(el, this.dom.firstChild);
9146                 return !returnDom ? Roo.get(el) : el;
9147             }
9148         },
9149
9150         /**
9151          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9152          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9153          * @param {String} where (optional) 'before' or 'after' defaults to before
9154          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9155          * @return {Roo.Element} the inserted Element
9156          */
9157         insertSibling: function(el, where, returnDom){
9158             where = where ? where.toLowerCase() : 'before';
9159             el = el || {};
9160             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9161
9162             if(typeof el == 'object' && !el.nodeType){ // dh config
9163                 if(where == 'after' && !this.dom.nextSibling){
9164                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9165                 }else{
9166                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9167                 }
9168
9169             }else{
9170                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9171                             where == 'before' ? this.dom : this.dom.nextSibling);
9172                 if(!returnDom){
9173                     rt = Roo.get(rt);
9174                 }
9175             }
9176             return rt;
9177         },
9178
9179         /**
9180          * Creates and wraps this element with another element
9181          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9182          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9183          * @return {HTMLElement/Element} The newly created wrapper element
9184          */
9185         wrap: function(config, returnDom){
9186             if(!config){
9187                 config = {tag: "div"};
9188             }
9189             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9190             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9191             return newEl;
9192         },
9193
9194         /**
9195          * Replaces the passed element with this element
9196          * @param {String/HTMLElement/Element} el The element to replace
9197          * @return {Roo.Element} this
9198          */
9199         replace: function(el){
9200             el = Roo.get(el);
9201             this.insertBefore(el);
9202             el.remove();
9203             return this;
9204         },
9205
9206         /**
9207          * Inserts an html fragment into this element
9208          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9209          * @param {String} html The HTML fragment
9210          * @param {Boolean} returnEl True to return an Roo.Element
9211          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9212          */
9213         insertHtml : function(where, html, returnEl){
9214             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9215             return returnEl ? Roo.get(el) : el;
9216         },
9217
9218         /**
9219          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9220          * @param {Object} o The object with the attributes
9221          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9222          * @return {Roo.Element} this
9223          */
9224         set : function(o, useSet){
9225             var el = this.dom;
9226             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9227             for(var attr in o){
9228                 if(attr == "style" || typeof o[attr] == "function") continue;
9229                 if(attr=="cls"){
9230                     el.className = o["cls"];
9231                 }else{
9232                     if(useSet) el.setAttribute(attr, o[attr]);
9233                     else el[attr] = o[attr];
9234                 }
9235             }
9236             if(o.style){
9237                 Roo.DomHelper.applyStyles(el, o.style);
9238             }
9239             return this;
9240         },
9241
9242         /**
9243          * Convenience method for constructing a KeyMap
9244          * @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:
9245          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9246          * @param {Function} fn The function to call
9247          * @param {Object} scope (optional) The scope of the function
9248          * @return {Roo.KeyMap} The KeyMap created
9249          */
9250         addKeyListener : function(key, fn, scope){
9251             var config;
9252             if(typeof key != "object" || key instanceof Array){
9253                 config = {
9254                     key: key,
9255                     fn: fn,
9256                     scope: scope
9257                 };
9258             }else{
9259                 config = {
9260                     key : key.key,
9261                     shift : key.shift,
9262                     ctrl : key.ctrl,
9263                     alt : key.alt,
9264                     fn: fn,
9265                     scope: scope
9266                 };
9267             }
9268             return new Roo.KeyMap(this, config);
9269         },
9270
9271         /**
9272          * Creates a KeyMap for this element
9273          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9274          * @return {Roo.KeyMap} The KeyMap created
9275          */
9276         addKeyMap : function(config){
9277             return new Roo.KeyMap(this, config);
9278         },
9279
9280         /**
9281          * Returns true if this element is scrollable.
9282          * @return {Boolean}
9283          */
9284          isScrollable : function(){
9285             var dom = this.dom;
9286             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9287         },
9288
9289         /**
9290          * 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().
9291          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9292          * @param {Number} value The new scroll value
9293          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9294          * @return {Element} this
9295          */
9296
9297         scrollTo : function(side, value, animate){
9298             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9299             if(!animate || !A){
9300                 this.dom[prop] = value;
9301             }else{
9302                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9303                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9304             }
9305             return this;
9306         },
9307
9308         /**
9309          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9310          * within this element's scrollable range.
9311          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9312          * @param {Number} distance How far to scroll the element in pixels
9313          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9314          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9315          * was scrolled as far as it could go.
9316          */
9317          scroll : function(direction, distance, animate){
9318              if(!this.isScrollable()){
9319                  return;
9320              }
9321              var el = this.dom;
9322              var l = el.scrollLeft, t = el.scrollTop;
9323              var w = el.scrollWidth, h = el.scrollHeight;
9324              var cw = el.clientWidth, ch = el.clientHeight;
9325              direction = direction.toLowerCase();
9326              var scrolled = false;
9327              var a = this.preanim(arguments, 2);
9328              switch(direction){
9329                  case "l":
9330                  case "left":
9331                      if(w - l > cw){
9332                          var v = Math.min(l + distance, w-cw);
9333                          this.scrollTo("left", v, a);
9334                          scrolled = true;
9335                      }
9336                      break;
9337                 case "r":
9338                 case "right":
9339                      if(l > 0){
9340                          var v = Math.max(l - distance, 0);
9341                          this.scrollTo("left", v, a);
9342                          scrolled = true;
9343                      }
9344                      break;
9345                 case "t":
9346                 case "top":
9347                 case "up":
9348                      if(t > 0){
9349                          var v = Math.max(t - distance, 0);
9350                          this.scrollTo("top", v, a);
9351                          scrolled = true;
9352                      }
9353                      break;
9354                 case "b":
9355                 case "bottom":
9356                 case "down":
9357                      if(h - t > ch){
9358                          var v = Math.min(t + distance, h-ch);
9359                          this.scrollTo("top", v, a);
9360                          scrolled = true;
9361                      }
9362                      break;
9363              }
9364              return scrolled;
9365         },
9366
9367         /**
9368          * Translates the passed page coordinates into left/top css values for this element
9369          * @param {Number/Array} x The page x or an array containing [x, y]
9370          * @param {Number} y The page y
9371          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9372          */
9373         translatePoints : function(x, y){
9374             if(typeof x == 'object' || x instanceof Array){
9375                 y = x[1]; x = x[0];
9376             }
9377             var p = this.getStyle('position');
9378             var o = this.getXY();
9379
9380             var l = parseInt(this.getStyle('left'), 10);
9381             var t = parseInt(this.getStyle('top'), 10);
9382
9383             if(isNaN(l)){
9384                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9385             }
9386             if(isNaN(t)){
9387                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9388             }
9389
9390             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9391         },
9392
9393         /**
9394          * Returns the current scroll position of the element.
9395          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9396          */
9397         getScroll : function(){
9398             var d = this.dom, doc = document;
9399             if(d == doc || d == doc.body){
9400                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9401                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9402                 return {left: l, top: t};
9403             }else{
9404                 return {left: d.scrollLeft, top: d.scrollTop};
9405             }
9406         },
9407
9408         /**
9409          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9410          * are convert to standard 6 digit hex color.
9411          * @param {String} attr The css attribute
9412          * @param {String} defaultValue The default value to use when a valid color isn't found
9413          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9414          * YUI color anims.
9415          */
9416         getColor : function(attr, defaultValue, prefix){
9417             var v = this.getStyle(attr);
9418             if(!v || v == "transparent" || v == "inherit") {
9419                 return defaultValue;
9420             }
9421             var color = typeof prefix == "undefined" ? "#" : prefix;
9422             if(v.substr(0, 4) == "rgb("){
9423                 var rvs = v.slice(4, v.length -1).split(",");
9424                 for(var i = 0; i < 3; i++){
9425                     var h = parseInt(rvs[i]).toString(16);
9426                     if(h < 16){
9427                         h = "0" + h;
9428                     }
9429                     color += h;
9430                 }
9431             } else {
9432                 if(v.substr(0, 1) == "#"){
9433                     if(v.length == 4) {
9434                         for(var i = 1; i < 4; i++){
9435                             var c = v.charAt(i);
9436                             color +=  c + c;
9437                         }
9438                     }else if(v.length == 7){
9439                         color += v.substr(1);
9440                     }
9441                 }
9442             }
9443             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9444         },
9445
9446         /**
9447          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9448          * gradient background, rounded corners and a 4-way shadow.
9449          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9450          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9451          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9452          * @return {Roo.Element} this
9453          */
9454         boxWrap : function(cls){
9455             cls = cls || 'x-box';
9456             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9457             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9458             return el;
9459         },
9460
9461         /**
9462          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9463          * @param {String} namespace The namespace in which to look for the attribute
9464          * @param {String} name The attribute name
9465          * @return {String} The attribute value
9466          */
9467         getAttributeNS : Roo.isIE ? function(ns, name){
9468             var d = this.dom;
9469             var type = typeof d[ns+":"+name];
9470             if(type != 'undefined' && type != 'unknown'){
9471                 return d[ns+":"+name];
9472             }
9473             return d[name];
9474         } : function(ns, name){
9475             var d = this.dom;
9476             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9477         }
9478     };
9479
9480     var ep = El.prototype;
9481
9482     /**
9483      * Appends an event handler (Shorthand for addListener)
9484      * @param {String}   eventName     The type of event to append
9485      * @param {Function} fn        The method the event invokes
9486      * @param {Object} scope       (optional) The scope (this object) of the fn
9487      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9488      * @method
9489      */
9490     ep.on = ep.addListener;
9491         // backwards compat
9492     ep.mon = ep.addListener;
9493
9494     /**
9495      * Removes an event handler from this element (shorthand for removeListener)
9496      * @param {String} eventName the type of event to remove
9497      * @param {Function} fn the method the event invokes
9498      * @return {Roo.Element} this
9499      * @method
9500      */
9501     ep.un = ep.removeListener;
9502
9503     /**
9504      * true to automatically adjust width and height settings for box-model issues (default to true)
9505      */
9506     ep.autoBoxAdjust = true;
9507
9508     // private
9509     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9510
9511     // private
9512     El.addUnits = function(v, defaultUnit){
9513         if(v === "" || v == "auto"){
9514             return v;
9515         }
9516         if(v === undefined){
9517             return '';
9518         }
9519         if(typeof v == "number" || !El.unitPattern.test(v)){
9520             return v + (defaultUnit || 'px');
9521         }
9522         return v;
9523     };
9524
9525     // special markup used throughout Roo when box wrapping elements
9526     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>';
9527     /**
9528      * Visibility mode constant - Use visibility to hide element
9529      * @static
9530      * @type Number
9531      */
9532     El.VISIBILITY = 1;
9533     /**
9534      * Visibility mode constant - Use display to hide element
9535      * @static
9536      * @type Number
9537      */
9538     El.DISPLAY = 2;
9539
9540     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9541     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9542     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9543
9544
9545
9546     /**
9547      * @private
9548      */
9549     El.cache = {};
9550
9551     var docEl;
9552
9553     /**
9554      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9555      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9556      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9557      * @return {Element} The Element object
9558      * @static
9559      */
9560     El.get = function(el){
9561         var ex, elm, id;
9562         if(!el){ return null; }
9563         if(typeof el == "string"){ // element id
9564             if(!(elm = document.getElementById(el))){
9565                 return null;
9566             }
9567             if(ex = El.cache[el]){
9568                 ex.dom = elm;
9569             }else{
9570                 ex = El.cache[el] = new El(elm);
9571             }
9572             return ex;
9573         }else if(el.tagName){ // dom element
9574             if(!(id = el.id)){
9575                 id = Roo.id(el);
9576             }
9577             if(ex = El.cache[id]){
9578                 ex.dom = el;
9579             }else{
9580                 ex = El.cache[id] = new El(el);
9581             }
9582             return ex;
9583         }else if(el instanceof El){
9584             if(el != docEl){
9585                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9586                                                               // catch case where it hasn't been appended
9587                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9588             }
9589             return el;
9590         }else if(el.isComposite){
9591             return el;
9592         }else if(el instanceof Array){
9593             return El.select(el);
9594         }else if(el == document){
9595             // create a bogus element object representing the document object
9596             if(!docEl){
9597                 var f = function(){};
9598                 f.prototype = El.prototype;
9599                 docEl = new f();
9600                 docEl.dom = document;
9601             }
9602             return docEl;
9603         }
9604         return null;
9605     };
9606
9607     // private
9608     El.uncache = function(el){
9609         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9610             if(a[i]){
9611                 delete El.cache[a[i].id || a[i]];
9612             }
9613         }
9614     };
9615
9616     // private
9617     // Garbage collection - uncache elements/purge listeners on orphaned elements
9618     // so we don't hold a reference and cause the browser to retain them
9619     El.garbageCollect = function(){
9620         if(!Roo.enableGarbageCollector){
9621             clearInterval(El.collectorThread);
9622             return;
9623         }
9624         for(var eid in El.cache){
9625             var el = El.cache[eid], d = el.dom;
9626             // -------------------------------------------------------
9627             // Determining what is garbage:
9628             // -------------------------------------------------------
9629             // !d
9630             // dom node is null, definitely garbage
9631             // -------------------------------------------------------
9632             // !d.parentNode
9633             // no parentNode == direct orphan, definitely garbage
9634             // -------------------------------------------------------
9635             // !d.offsetParent && !document.getElementById(eid)
9636             // display none elements have no offsetParent so we will
9637             // also try to look it up by it's id. However, check
9638             // offsetParent first so we don't do unneeded lookups.
9639             // This enables collection of elements that are not orphans
9640             // directly, but somewhere up the line they have an orphan
9641             // parent.
9642             // -------------------------------------------------------
9643             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9644                 delete El.cache[eid];
9645                 if(d && Roo.enableListenerCollection){
9646                     E.purgeElement(d);
9647                 }
9648             }
9649         }
9650     }
9651     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9652
9653
9654     // dom is optional
9655     El.Flyweight = function(dom){
9656         this.dom = dom;
9657     };
9658     El.Flyweight.prototype = El.prototype;
9659
9660     El._flyweights = {};
9661     /**
9662      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9663      * the dom node can be overwritten by other code.
9664      * @param {String/HTMLElement} el The dom node or id
9665      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9666      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9667      * @static
9668      * @return {Element} The shared Element object
9669      */
9670     El.fly = function(el, named){
9671         named = named || '_global';
9672         el = Roo.getDom(el);
9673         if(!el){
9674             return null;
9675         }
9676         if(!El._flyweights[named]){
9677             El._flyweights[named] = new El.Flyweight();
9678         }
9679         El._flyweights[named].dom = el;
9680         return El._flyweights[named];
9681     };
9682
9683     /**
9684      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9685      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9686      * Shorthand of {@link Roo.Element#get}
9687      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9688      * @return {Element} The Element object
9689      * @member Roo
9690      * @method get
9691      */
9692     Roo.get = El.get;
9693     /**
9694      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9695      * the dom node can be overwritten by other code.
9696      * Shorthand of {@link Roo.Element#fly}
9697      * @param {String/HTMLElement} el The dom node or id
9698      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9699      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9700      * @static
9701      * @return {Element} The shared Element object
9702      * @member Roo
9703      * @method fly
9704      */
9705     Roo.fly = El.fly;
9706
9707     // speedy lookup for elements never to box adjust
9708     var noBoxAdjust = Roo.isStrict ? {
9709         select:1
9710     } : {
9711         input:1, select:1, textarea:1
9712     };
9713     if(Roo.isIE || Roo.isGecko){
9714         noBoxAdjust['button'] = 1;
9715     }
9716
9717
9718     Roo.EventManager.on(window, 'unload', function(){
9719         delete El.cache;
9720         delete El._flyweights;
9721     });
9722 })();
9723
9724
9725
9726
9727 if(Roo.DomQuery){
9728     Roo.Element.selectorFunction = Roo.DomQuery.select;
9729 }
9730
9731 Roo.Element.select = function(selector, unique, root){
9732     var els;
9733     if(typeof selector == "string"){
9734         els = Roo.Element.selectorFunction(selector, root);
9735     }else if(selector.length !== undefined){
9736         els = selector;
9737     }else{
9738         throw "Invalid selector";
9739     }
9740     if(unique === true){
9741         return new Roo.CompositeElement(els);
9742     }else{
9743         return new Roo.CompositeElementLite(els);
9744     }
9745 };
9746 /**
9747  * Selects elements based on the passed CSS selector to enable working on them as 1.
9748  * @param {String/Array} selector The CSS selector or an array of elements
9749  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
9750  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9751  * @return {CompositeElementLite/CompositeElement}
9752  * @member Roo
9753  * @method select
9754  */
9755 Roo.select = Roo.Element.select;
9756
9757
9758
9759
9760
9761
9762
9763
9764
9765
9766
9767
9768
9769
9770 /*
9771  * Based on:
9772  * Ext JS Library 1.1.1
9773  * Copyright(c) 2006-2007, Ext JS, LLC.
9774  *
9775  * Originally Released Under LGPL - original licence link has changed is not relivant.
9776  *
9777  * Fork - LGPL
9778  * <script type="text/javascript">
9779  */
9780
9781
9782
9783 //Notifies Element that fx methods are available
9784 Roo.enableFx = true;
9785
9786 /**
9787  * @class Roo.Fx
9788  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
9789  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
9790  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
9791  * Element effects to work.</p><br/>
9792  *
9793  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
9794  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
9795  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
9796  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
9797  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
9798  * expected results and should be done with care.</p><br/>
9799  *
9800  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
9801  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
9802 <pre>
9803 Value  Description
9804 -----  -----------------------------
9805 tl     The top left corner
9806 t      The center of the top edge
9807 tr     The top right corner
9808 l      The center of the left edge
9809 r      The center of the right edge
9810 bl     The bottom left corner
9811 b      The center of the bottom edge
9812 br     The bottom right corner
9813 </pre>
9814  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
9815  * below are common options that can be passed to any Fx method.</b>
9816  * @cfg {Function} callback A function called when the effect is finished
9817  * @cfg {Object} scope The scope of the effect function
9818  * @cfg {String} easing A valid Easing value for the effect
9819  * @cfg {String} afterCls A css class to apply after the effect
9820  * @cfg {Number} duration The length of time (in seconds) that the effect should last
9821  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
9822  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
9823  * effects that end with the element being visually hidden, ignored otherwise)
9824  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
9825  * a function which returns such a specification that will be applied to the Element after the effect finishes
9826  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
9827  * @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
9828  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
9829  */
9830 Roo.Fx = {
9831         /**
9832          * Slides the element into view.  An anchor point can be optionally passed to set the point of
9833          * origin for the slide effect.  This function automatically handles wrapping the element with
9834          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9835          * Usage:
9836          *<pre><code>
9837 // default: slide the element in from the top
9838 el.slideIn();
9839
9840 // custom: slide the element in from the right with a 2-second duration
9841 el.slideIn('r', { duration: 2 });
9842
9843 // common config options shown with default values
9844 el.slideIn('t', {
9845     easing: 'easeOut',
9846     duration: .5
9847 });
9848 </code></pre>
9849          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9850          * @param {Object} options (optional) Object literal with any of the Fx config options
9851          * @return {Roo.Element} The Element
9852          */
9853     slideIn : function(anchor, o){
9854         var el = this.getFxEl();
9855         o = o || {};
9856
9857         el.queueFx(o, function(){
9858
9859             anchor = anchor || "t";
9860
9861             // fix display to visibility
9862             this.fixDisplay();
9863
9864             // restore values after effect
9865             var r = this.getFxRestore();
9866             var b = this.getBox();
9867             // fixed size for slide
9868             this.setSize(b);
9869
9870             // wrap if needed
9871             var wrap = this.fxWrap(r.pos, o, "hidden");
9872
9873             var st = this.dom.style;
9874             st.visibility = "visible";
9875             st.position = "absolute";
9876
9877             // clear out temp styles after slide and unwrap
9878             var after = function(){
9879                 el.fxUnwrap(wrap, r.pos, o);
9880                 st.width = r.width;
9881                 st.height = r.height;
9882                 el.afterFx(o);
9883             };
9884             // time to calc the positions
9885             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
9886
9887             switch(anchor.toLowerCase()){
9888                 case "t":
9889                     wrap.setSize(b.width, 0);
9890                     st.left = st.bottom = "0";
9891                     a = {height: bh};
9892                 break;
9893                 case "l":
9894                     wrap.setSize(0, b.height);
9895                     st.right = st.top = "0";
9896                     a = {width: bw};
9897                 break;
9898                 case "r":
9899                     wrap.setSize(0, b.height);
9900                     wrap.setX(b.right);
9901                     st.left = st.top = "0";
9902                     a = {width: bw, points: pt};
9903                 break;
9904                 case "b":
9905                     wrap.setSize(b.width, 0);
9906                     wrap.setY(b.bottom);
9907                     st.left = st.top = "0";
9908                     a = {height: bh, points: pt};
9909                 break;
9910                 case "tl":
9911                     wrap.setSize(0, 0);
9912                     st.right = st.bottom = "0";
9913                     a = {width: bw, height: bh};
9914                 break;
9915                 case "bl":
9916                     wrap.setSize(0, 0);
9917                     wrap.setY(b.y+b.height);
9918                     st.right = st.top = "0";
9919                     a = {width: bw, height: bh, points: pt};
9920                 break;
9921                 case "br":
9922                     wrap.setSize(0, 0);
9923                     wrap.setXY([b.right, b.bottom]);
9924                     st.left = st.top = "0";
9925                     a = {width: bw, height: bh, points: pt};
9926                 break;
9927                 case "tr":
9928                     wrap.setSize(0, 0);
9929                     wrap.setX(b.x+b.width);
9930                     st.left = st.bottom = "0";
9931                     a = {width: bw, height: bh, points: pt};
9932                 break;
9933             }
9934             this.dom.style.visibility = "visible";
9935             wrap.show();
9936
9937             arguments.callee.anim = wrap.fxanim(a,
9938                 o,
9939                 'motion',
9940                 .5,
9941                 'easeOut', after);
9942         });
9943         return this;
9944     },
9945     
9946         /**
9947          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
9948          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
9949          * 'hidden') but block elements will still take up space in the document.  The element must be removed
9950          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
9951          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9952          * Usage:
9953          *<pre><code>
9954 // default: slide the element out to the top
9955 el.slideOut();
9956
9957 // custom: slide the element out to the right with a 2-second duration
9958 el.slideOut('r', { duration: 2 });
9959
9960 // common config options shown with default values
9961 el.slideOut('t', {
9962     easing: 'easeOut',
9963     duration: .5,
9964     remove: false,
9965     useDisplay: false
9966 });
9967 </code></pre>
9968          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9969          * @param {Object} options (optional) Object literal with any of the Fx config options
9970          * @return {Roo.Element} The Element
9971          */
9972     slideOut : function(anchor, o){
9973         var el = this.getFxEl();
9974         o = o || {};
9975
9976         el.queueFx(o, function(){
9977
9978             anchor = anchor || "t";
9979
9980             // restore values after effect
9981             var r = this.getFxRestore();
9982             
9983             var b = this.getBox();
9984             // fixed size for slide
9985             this.setSize(b);
9986
9987             // wrap if needed
9988             var wrap = this.fxWrap(r.pos, o, "visible");
9989
9990             var st = this.dom.style;
9991             st.visibility = "visible";
9992             st.position = "absolute";
9993
9994             wrap.setSize(b);
9995
9996             var after = function(){
9997                 if(o.useDisplay){
9998                     el.setDisplayed(false);
9999                 }else{
10000                     el.hide();
10001                 }
10002
10003                 el.fxUnwrap(wrap, r.pos, o);
10004
10005                 st.width = r.width;
10006                 st.height = r.height;
10007
10008                 el.afterFx(o);
10009             };
10010
10011             var a, zero = {to: 0};
10012             switch(anchor.toLowerCase()){
10013                 case "t":
10014                     st.left = st.bottom = "0";
10015                     a = {height: zero};
10016                 break;
10017                 case "l":
10018                     st.right = st.top = "0";
10019                     a = {width: zero};
10020                 break;
10021                 case "r":
10022                     st.left = st.top = "0";
10023                     a = {width: zero, points: {to:[b.right, b.y]}};
10024                 break;
10025                 case "b":
10026                     st.left = st.top = "0";
10027                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10028                 break;
10029                 case "tl":
10030                     st.right = st.bottom = "0";
10031                     a = {width: zero, height: zero};
10032                 break;
10033                 case "bl":
10034                     st.right = st.top = "0";
10035                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10036                 break;
10037                 case "br":
10038                     st.left = st.top = "0";
10039                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10040                 break;
10041                 case "tr":
10042                     st.left = st.bottom = "0";
10043                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10044                 break;
10045             }
10046
10047             arguments.callee.anim = wrap.fxanim(a,
10048                 o,
10049                 'motion',
10050                 .5,
10051                 "easeOut", after);
10052         });
10053         return this;
10054     },
10055
10056         /**
10057          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10058          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10059          * The element must be removed from the DOM using the 'remove' config option if desired.
10060          * Usage:
10061          *<pre><code>
10062 // default
10063 el.puff();
10064
10065 // common config options shown with default values
10066 el.puff({
10067     easing: 'easeOut',
10068     duration: .5,
10069     remove: false,
10070     useDisplay: false
10071 });
10072 </code></pre>
10073          * @param {Object} options (optional) Object literal with any of the Fx config options
10074          * @return {Roo.Element} The Element
10075          */
10076     puff : function(o){
10077         var el = this.getFxEl();
10078         o = o || {};
10079
10080         el.queueFx(o, function(){
10081             this.clearOpacity();
10082             this.show();
10083
10084             // restore values after effect
10085             var r = this.getFxRestore();
10086             var st = this.dom.style;
10087
10088             var after = function(){
10089                 if(o.useDisplay){
10090                     el.setDisplayed(false);
10091                 }else{
10092                     el.hide();
10093                 }
10094
10095                 el.clearOpacity();
10096
10097                 el.setPositioning(r.pos);
10098                 st.width = r.width;
10099                 st.height = r.height;
10100                 st.fontSize = '';
10101                 el.afterFx(o);
10102             };
10103
10104             var width = this.getWidth();
10105             var height = this.getHeight();
10106
10107             arguments.callee.anim = this.fxanim({
10108                     width : {to: this.adjustWidth(width * 2)},
10109                     height : {to: this.adjustHeight(height * 2)},
10110                     points : {by: [-(width * .5), -(height * .5)]},
10111                     opacity : {to: 0},
10112                     fontSize: {to:200, unit: "%"}
10113                 },
10114                 o,
10115                 'motion',
10116                 .5,
10117                 "easeOut", after);
10118         });
10119         return this;
10120     },
10121
10122         /**
10123          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10124          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10125          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10126          * Usage:
10127          *<pre><code>
10128 // default
10129 el.switchOff();
10130
10131 // all config options shown with default values
10132 el.switchOff({
10133     easing: 'easeIn',
10134     duration: .3,
10135     remove: false,
10136     useDisplay: false
10137 });
10138 </code></pre>
10139          * @param {Object} options (optional) Object literal with any of the Fx config options
10140          * @return {Roo.Element} The Element
10141          */
10142     switchOff : function(o){
10143         var el = this.getFxEl();
10144         o = o || {};
10145
10146         el.queueFx(o, function(){
10147             this.clearOpacity();
10148             this.clip();
10149
10150             // restore values after effect
10151             var r = this.getFxRestore();
10152             var st = this.dom.style;
10153
10154             var after = function(){
10155                 if(o.useDisplay){
10156                     el.setDisplayed(false);
10157                 }else{
10158                     el.hide();
10159                 }
10160
10161                 el.clearOpacity();
10162                 el.setPositioning(r.pos);
10163                 st.width = r.width;
10164                 st.height = r.height;
10165
10166                 el.afterFx(o);
10167             };
10168
10169             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10170                 this.clearOpacity();
10171                 (function(){
10172                     this.fxanim({
10173                         height:{to:1},
10174                         points:{by:[0, this.getHeight() * .5]}
10175                     }, o, 'motion', 0.3, 'easeIn', after);
10176                 }).defer(100, this);
10177             });
10178         });
10179         return this;
10180     },
10181
10182     /**
10183      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10184      * changed using the "attr" config option) and then fading back to the original color. If no original
10185      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10186      * Usage:
10187 <pre><code>
10188 // default: highlight background to yellow
10189 el.highlight();
10190
10191 // custom: highlight foreground text to blue for 2 seconds
10192 el.highlight("0000ff", { attr: 'color', duration: 2 });
10193
10194 // common config options shown with default values
10195 el.highlight("ffff9c", {
10196     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10197     endColor: (current color) or "ffffff",
10198     easing: 'easeIn',
10199     duration: 1
10200 });
10201 </code></pre>
10202      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10203      * @param {Object} options (optional) Object literal with any of the Fx config options
10204      * @return {Roo.Element} The Element
10205      */ 
10206     highlight : function(color, o){
10207         var el = this.getFxEl();
10208         o = o || {};
10209
10210         el.queueFx(o, function(){
10211             color = color || "ffff9c";
10212             attr = o.attr || "backgroundColor";
10213
10214             this.clearOpacity();
10215             this.show();
10216
10217             var origColor = this.getColor(attr);
10218             var restoreColor = this.dom.style[attr];
10219             endColor = (o.endColor || origColor) || "ffffff";
10220
10221             var after = function(){
10222                 el.dom.style[attr] = restoreColor;
10223                 el.afterFx(o);
10224             };
10225
10226             var a = {};
10227             a[attr] = {from: color, to: endColor};
10228             arguments.callee.anim = this.fxanim(a,
10229                 o,
10230                 'color',
10231                 1,
10232                 'easeIn', after);
10233         });
10234         return this;
10235     },
10236
10237    /**
10238     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10239     * Usage:
10240 <pre><code>
10241 // default: a single light blue ripple
10242 el.frame();
10243
10244 // custom: 3 red ripples lasting 3 seconds total
10245 el.frame("ff0000", 3, { duration: 3 });
10246
10247 // common config options shown with default values
10248 el.frame("C3DAF9", 1, {
10249     duration: 1 //duration of entire animation (not each individual ripple)
10250     // Note: Easing is not configurable and will be ignored if included
10251 });
10252 </code></pre>
10253     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10254     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10255     * @param {Object} options (optional) Object literal with any of the Fx config options
10256     * @return {Roo.Element} The Element
10257     */
10258     frame : function(color, count, o){
10259         var el = this.getFxEl();
10260         o = o || {};
10261
10262         el.queueFx(o, function(){
10263             color = color || "#C3DAF9";
10264             if(color.length == 6){
10265                 color = "#" + color;
10266             }
10267             count = count || 1;
10268             duration = o.duration || 1;
10269             this.show();
10270
10271             var b = this.getBox();
10272             var animFn = function(){
10273                 var proxy = this.createProxy({
10274
10275                      style:{
10276                         visbility:"hidden",
10277                         position:"absolute",
10278                         "z-index":"35000", // yee haw
10279                         border:"0px solid " + color
10280                      }
10281                   });
10282                 var scale = Roo.isBorderBox ? 2 : 1;
10283                 proxy.animate({
10284                     top:{from:b.y, to:b.y - 20},
10285                     left:{from:b.x, to:b.x - 20},
10286                     borderWidth:{from:0, to:10},
10287                     opacity:{from:1, to:0},
10288                     height:{from:b.height, to:(b.height + (20*scale))},
10289                     width:{from:b.width, to:(b.width + (20*scale))}
10290                 }, duration, function(){
10291                     proxy.remove();
10292                 });
10293                 if(--count > 0){
10294                      animFn.defer((duration/2)*1000, this);
10295                 }else{
10296                     el.afterFx(o);
10297                 }
10298             };
10299             animFn.call(this);
10300         });
10301         return this;
10302     },
10303
10304    /**
10305     * Creates a pause before any subsequent queued effects begin.  If there are
10306     * no effects queued after the pause it will have no effect.
10307     * Usage:
10308 <pre><code>
10309 el.pause(1);
10310 </code></pre>
10311     * @param {Number} seconds The length of time to pause (in seconds)
10312     * @return {Roo.Element} The Element
10313     */
10314     pause : function(seconds){
10315         var el = this.getFxEl();
10316         var o = {};
10317
10318         el.queueFx(o, function(){
10319             setTimeout(function(){
10320                 el.afterFx(o);
10321             }, seconds * 1000);
10322         });
10323         return this;
10324     },
10325
10326    /**
10327     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10328     * using the "endOpacity" config option.
10329     * Usage:
10330 <pre><code>
10331 // default: fade in from opacity 0 to 100%
10332 el.fadeIn();
10333
10334 // custom: fade in from opacity 0 to 75% over 2 seconds
10335 el.fadeIn({ endOpacity: .75, duration: 2});
10336
10337 // common config options shown with default values
10338 el.fadeIn({
10339     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10340     easing: 'easeOut',
10341     duration: .5
10342 });
10343 </code></pre>
10344     * @param {Object} options (optional) Object literal with any of the Fx config options
10345     * @return {Roo.Element} The Element
10346     */
10347     fadeIn : function(o){
10348         var el = this.getFxEl();
10349         o = o || {};
10350         el.queueFx(o, function(){
10351             this.setOpacity(0);
10352             this.fixDisplay();
10353             this.dom.style.visibility = 'visible';
10354             var to = o.endOpacity || 1;
10355             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10356                 o, null, .5, "easeOut", function(){
10357                 if(to == 1){
10358                     this.clearOpacity();
10359                 }
10360                 el.afterFx(o);
10361             });
10362         });
10363         return this;
10364     },
10365
10366    /**
10367     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10368     * using the "endOpacity" config option.
10369     * Usage:
10370 <pre><code>
10371 // default: fade out from the element's current opacity to 0
10372 el.fadeOut();
10373
10374 // custom: fade out from the element's current opacity to 25% over 2 seconds
10375 el.fadeOut({ endOpacity: .25, duration: 2});
10376
10377 // common config options shown with default values
10378 el.fadeOut({
10379     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10380     easing: 'easeOut',
10381     duration: .5
10382     remove: false,
10383     useDisplay: false
10384 });
10385 </code></pre>
10386     * @param {Object} options (optional) Object literal with any of the Fx config options
10387     * @return {Roo.Element} The Element
10388     */
10389     fadeOut : function(o){
10390         var el = this.getFxEl();
10391         o = o || {};
10392         el.queueFx(o, function(){
10393             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10394                 o, null, .5, "easeOut", function(){
10395                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10396                      this.dom.style.display = "none";
10397                 }else{
10398                      this.dom.style.visibility = "hidden";
10399                 }
10400                 this.clearOpacity();
10401                 el.afterFx(o);
10402             });
10403         });
10404         return this;
10405     },
10406
10407    /**
10408     * Animates the transition of an element's dimensions from a starting height/width
10409     * to an ending height/width.
10410     * Usage:
10411 <pre><code>
10412 // change height and width to 100x100 pixels
10413 el.scale(100, 100);
10414
10415 // common config options shown with default values.  The height and width will default to
10416 // the element's existing values if passed as null.
10417 el.scale(
10418     [element's width],
10419     [element's height], {
10420     easing: 'easeOut',
10421     duration: .35
10422 });
10423 </code></pre>
10424     * @param {Number} width  The new width (pass undefined to keep the original width)
10425     * @param {Number} height  The new height (pass undefined to keep the original height)
10426     * @param {Object} options (optional) Object literal with any of the Fx config options
10427     * @return {Roo.Element} The Element
10428     */
10429     scale : function(w, h, o){
10430         this.shift(Roo.apply({}, o, {
10431             width: w,
10432             height: h
10433         }));
10434         return this;
10435     },
10436
10437    /**
10438     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10439     * Any of these properties not specified in the config object will not be changed.  This effect 
10440     * requires that at least one new dimension, position or opacity setting must be passed in on
10441     * the config object in order for the function to have any effect.
10442     * Usage:
10443 <pre><code>
10444 // slide the element horizontally to x position 200 while changing the height and opacity
10445 el.shift({ x: 200, height: 50, opacity: .8 });
10446
10447 // common config options shown with default values.
10448 el.shift({
10449     width: [element's width],
10450     height: [element's height],
10451     x: [element's x position],
10452     y: [element's y position],
10453     opacity: [element's opacity],
10454     easing: 'easeOut',
10455     duration: .35
10456 });
10457 </code></pre>
10458     * @param {Object} options  Object literal with any of the Fx config options
10459     * @return {Roo.Element} The Element
10460     */
10461     shift : function(o){
10462         var el = this.getFxEl();
10463         o = o || {};
10464         el.queueFx(o, function(){
10465             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10466             if(w !== undefined){
10467                 a.width = {to: this.adjustWidth(w)};
10468             }
10469             if(h !== undefined){
10470                 a.height = {to: this.adjustHeight(h)};
10471             }
10472             if(x !== undefined || y !== undefined){
10473                 a.points = {to: [
10474                     x !== undefined ? x : this.getX(),
10475                     y !== undefined ? y : this.getY()
10476                 ]};
10477             }
10478             if(op !== undefined){
10479                 a.opacity = {to: op};
10480             }
10481             if(o.xy !== undefined){
10482                 a.points = {to: o.xy};
10483             }
10484             arguments.callee.anim = this.fxanim(a,
10485                 o, 'motion', .35, "easeOut", function(){
10486                 el.afterFx(o);
10487             });
10488         });
10489         return this;
10490     },
10491
10492         /**
10493          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10494          * ending point of the effect.
10495          * Usage:
10496          *<pre><code>
10497 // default: slide the element downward while fading out
10498 el.ghost();
10499
10500 // custom: slide the element out to the right with a 2-second duration
10501 el.ghost('r', { duration: 2 });
10502
10503 // common config options shown with default values
10504 el.ghost('b', {
10505     easing: 'easeOut',
10506     duration: .5
10507     remove: false,
10508     useDisplay: false
10509 });
10510 </code></pre>
10511          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10512          * @param {Object} options (optional) Object literal with any of the Fx config options
10513          * @return {Roo.Element} The Element
10514          */
10515     ghost : function(anchor, o){
10516         var el = this.getFxEl();
10517         o = o || {};
10518
10519         el.queueFx(o, function(){
10520             anchor = anchor || "b";
10521
10522             // restore values after effect
10523             var r = this.getFxRestore();
10524             var w = this.getWidth(),
10525                 h = this.getHeight();
10526
10527             var st = this.dom.style;
10528
10529             var after = function(){
10530                 if(o.useDisplay){
10531                     el.setDisplayed(false);
10532                 }else{
10533                     el.hide();
10534                 }
10535
10536                 el.clearOpacity();
10537                 el.setPositioning(r.pos);
10538                 st.width = r.width;
10539                 st.height = r.height;
10540
10541                 el.afterFx(o);
10542             };
10543
10544             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10545             switch(anchor.toLowerCase()){
10546                 case "t":
10547                     pt.by = [0, -h];
10548                 break;
10549                 case "l":
10550                     pt.by = [-w, 0];
10551                 break;
10552                 case "r":
10553                     pt.by = [w, 0];
10554                 break;
10555                 case "b":
10556                     pt.by = [0, h];
10557                 break;
10558                 case "tl":
10559                     pt.by = [-w, -h];
10560                 break;
10561                 case "bl":
10562                     pt.by = [-w, h];
10563                 break;
10564                 case "br":
10565                     pt.by = [w, h];
10566                 break;
10567                 case "tr":
10568                     pt.by = [w, -h];
10569                 break;
10570             }
10571
10572             arguments.callee.anim = this.fxanim(a,
10573                 o,
10574                 'motion',
10575                 .5,
10576                 "easeOut", after);
10577         });
10578         return this;
10579     },
10580
10581         /**
10582          * Ensures that all effects queued after syncFx is called on the element are
10583          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10584          * @return {Roo.Element} The Element
10585          */
10586     syncFx : function(){
10587         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10588             block : false,
10589             concurrent : true,
10590             stopFx : false
10591         });
10592         return this;
10593     },
10594
10595         /**
10596          * Ensures that all effects queued after sequenceFx is called on the element are
10597          * run in sequence.  This is the opposite of {@link #syncFx}.
10598          * @return {Roo.Element} The Element
10599          */
10600     sequenceFx : function(){
10601         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10602             block : false,
10603             concurrent : false,
10604             stopFx : false
10605         });
10606         return this;
10607     },
10608
10609         /* @private */
10610     nextFx : function(){
10611         var ef = this.fxQueue[0];
10612         if(ef){
10613             ef.call(this);
10614         }
10615     },
10616
10617         /**
10618          * Returns true if the element has any effects actively running or queued, else returns false.
10619          * @return {Boolean} True if element has active effects, else false
10620          */
10621     hasActiveFx : function(){
10622         return this.fxQueue && this.fxQueue[0];
10623     },
10624
10625         /**
10626          * Stops any running effects and clears the element's internal effects queue if it contains
10627          * any additional effects that haven't started yet.
10628          * @return {Roo.Element} The Element
10629          */
10630     stopFx : function(){
10631         if(this.hasActiveFx()){
10632             var cur = this.fxQueue[0];
10633             if(cur && cur.anim && cur.anim.isAnimated()){
10634                 this.fxQueue = [cur]; // clear out others
10635                 cur.anim.stop(true);
10636             }
10637         }
10638         return this;
10639     },
10640
10641         /* @private */
10642     beforeFx : function(o){
10643         if(this.hasActiveFx() && !o.concurrent){
10644            if(o.stopFx){
10645                this.stopFx();
10646                return true;
10647            }
10648            return false;
10649         }
10650         return true;
10651     },
10652
10653         /**
10654          * Returns true if the element is currently blocking so that no other effect can be queued
10655          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10656          * used to ensure that an effect initiated by a user action runs to completion prior to the
10657          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10658          * @return {Boolean} True if blocking, else false
10659          */
10660     hasFxBlock : function(){
10661         var q = this.fxQueue;
10662         return q && q[0] && q[0].block;
10663     },
10664
10665         /* @private */
10666     queueFx : function(o, fn){
10667         if(!this.fxQueue){
10668             this.fxQueue = [];
10669         }
10670         if(!this.hasFxBlock()){
10671             Roo.applyIf(o, this.fxDefaults);
10672             if(!o.concurrent){
10673                 var run = this.beforeFx(o);
10674                 fn.block = o.block;
10675                 this.fxQueue.push(fn);
10676                 if(run){
10677                     this.nextFx();
10678                 }
10679             }else{
10680                 fn.call(this);
10681             }
10682         }
10683         return this;
10684     },
10685
10686         /* @private */
10687     fxWrap : function(pos, o, vis){
10688         var wrap;
10689         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10690             var wrapXY;
10691             if(o.fixPosition){
10692                 wrapXY = this.getXY();
10693             }
10694             var div = document.createElement("div");
10695             div.style.visibility = vis;
10696             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10697             wrap.setPositioning(pos);
10698             if(wrap.getStyle("position") == "static"){
10699                 wrap.position("relative");
10700             }
10701             this.clearPositioning('auto');
10702             wrap.clip();
10703             wrap.dom.appendChild(this.dom);
10704             if(wrapXY){
10705                 wrap.setXY(wrapXY);
10706             }
10707         }
10708         return wrap;
10709     },
10710
10711         /* @private */
10712     fxUnwrap : function(wrap, pos, o){
10713         this.clearPositioning();
10714         this.setPositioning(pos);
10715         if(!o.wrap){
10716             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
10717             wrap.remove();
10718         }
10719     },
10720
10721         /* @private */
10722     getFxRestore : function(){
10723         var st = this.dom.style;
10724         return {pos: this.getPositioning(), width: st.width, height : st.height};
10725     },
10726
10727         /* @private */
10728     afterFx : function(o){
10729         if(o.afterStyle){
10730             this.applyStyles(o.afterStyle);
10731         }
10732         if(o.afterCls){
10733             this.addClass(o.afterCls);
10734         }
10735         if(o.remove === true){
10736             this.remove();
10737         }
10738         Roo.callback(o.callback, o.scope, [this]);
10739         if(!o.concurrent){
10740             this.fxQueue.shift();
10741             this.nextFx();
10742         }
10743     },
10744
10745         /* @private */
10746     getFxEl : function(){ // support for composite element fx
10747         return Roo.get(this.dom);
10748     },
10749
10750         /* @private */
10751     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
10752         animType = animType || 'run';
10753         opt = opt || {};
10754         var anim = Roo.lib.Anim[animType](
10755             this.dom, args,
10756             (opt.duration || defaultDur) || .35,
10757             (opt.easing || defaultEase) || 'easeOut',
10758             function(){
10759                 Roo.callback(cb, this);
10760             },
10761             this
10762         );
10763         opt.anim = anim;
10764         return anim;
10765     }
10766 };
10767
10768 // backwords compat
10769 Roo.Fx.resize = Roo.Fx.scale;
10770
10771 //When included, Roo.Fx is automatically applied to Element so that all basic
10772 //effects are available directly via the Element API
10773 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
10774  * Based on:
10775  * Ext JS Library 1.1.1
10776  * Copyright(c) 2006-2007, Ext JS, LLC.
10777  *
10778  * Originally Released Under LGPL - original licence link has changed is not relivant.
10779  *
10780  * Fork - LGPL
10781  * <script type="text/javascript">
10782  */
10783
10784
10785 /**
10786  * @class Roo.CompositeElement
10787  * Standard composite class. Creates a Roo.Element for every element in the collection.
10788  * <br><br>
10789  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10790  * actions will be performed on all the elements in this collection.</b>
10791  * <br><br>
10792  * All methods return <i>this</i> and can be chained.
10793  <pre><code>
10794  var els = Roo.select("#some-el div.some-class", true);
10795  // or select directly from an existing element
10796  var el = Roo.get('some-el');
10797  el.select('div.some-class', true);
10798
10799  els.setWidth(100); // all elements become 100 width
10800  els.hide(true); // all elements fade out and hide
10801  // or
10802  els.setWidth(100).hide(true);
10803  </code></pre>
10804  */
10805 Roo.CompositeElement = function(els){
10806     this.elements = [];
10807     this.addElements(els);
10808 };
10809 Roo.CompositeElement.prototype = {
10810     isComposite: true,
10811     addElements : function(els){
10812         if(!els) return this;
10813         if(typeof els == "string"){
10814             els = Roo.Element.selectorFunction(els);
10815         }
10816         var yels = this.elements;
10817         var index = yels.length-1;
10818         for(var i = 0, len = els.length; i < len; i++) {
10819                 yels[++index] = Roo.get(els[i]);
10820         }
10821         return this;
10822     },
10823
10824     /**
10825     * Clears this composite and adds the elements returned by the passed selector.
10826     * @param {String/Array} els A string CSS selector, an array of elements or an element
10827     * @return {CompositeElement} this
10828     */
10829     fill : function(els){
10830         this.elements = [];
10831         this.add(els);
10832         return this;
10833     },
10834
10835     /**
10836     * Filters this composite to only elements that match the passed selector.
10837     * @param {String} selector A string CSS selector
10838     * @return {CompositeElement} this
10839     */
10840     filter : function(selector){
10841         var els = [];
10842         this.each(function(el){
10843             if(el.is(selector)){
10844                 els[els.length] = el.dom;
10845             }
10846         });
10847         this.fill(els);
10848         return this;
10849     },
10850
10851     invoke : function(fn, args){
10852         var els = this.elements;
10853         for(var i = 0, len = els.length; i < len; i++) {
10854                 Roo.Element.prototype[fn].apply(els[i], args);
10855         }
10856         return this;
10857     },
10858     /**
10859     * Adds elements to this composite.
10860     * @param {String/Array} els A string CSS selector, an array of elements or an element
10861     * @return {CompositeElement} this
10862     */
10863     add : function(els){
10864         if(typeof els == "string"){
10865             this.addElements(Roo.Element.selectorFunction(els));
10866         }else if(els.length !== undefined){
10867             this.addElements(els);
10868         }else{
10869             this.addElements([els]);
10870         }
10871         return this;
10872     },
10873     /**
10874     * Calls the passed function passing (el, this, index) for each element in this composite.
10875     * @param {Function} fn The function to call
10876     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
10877     * @return {CompositeElement} this
10878     */
10879     each : function(fn, scope){
10880         var els = this.elements;
10881         for(var i = 0, len = els.length; i < len; i++){
10882             if(fn.call(scope || els[i], els[i], this, i) === false) {
10883                 break;
10884             }
10885         }
10886         return this;
10887     },
10888
10889     /**
10890      * Returns the Element object at the specified index
10891      * @param {Number} index
10892      * @return {Roo.Element}
10893      */
10894     item : function(index){
10895         return this.elements[index] || null;
10896     },
10897
10898     /**
10899      * Returns the first Element
10900      * @return {Roo.Element}
10901      */
10902     first : function(){
10903         return this.item(0);
10904     },
10905
10906     /**
10907      * Returns the last Element
10908      * @return {Roo.Element}
10909      */
10910     last : function(){
10911         return this.item(this.elements.length-1);
10912     },
10913
10914     /**
10915      * Returns the number of elements in this composite
10916      * @return Number
10917      */
10918     getCount : function(){
10919         return this.elements.length;
10920     },
10921
10922     /**
10923      * Returns true if this composite contains the passed element
10924      * @return Boolean
10925      */
10926     contains : function(el){
10927         return this.indexOf(el) !== -1;
10928     },
10929
10930     /**
10931      * Returns true if this composite contains the passed element
10932      * @return Boolean
10933      */
10934     indexOf : function(el){
10935         return this.elements.indexOf(Roo.get(el));
10936     },
10937
10938
10939     /**
10940     * Removes the specified element(s).
10941     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
10942     * or an array of any of those.
10943     * @param {Boolean} removeDom (optional) True to also remove the element from the document
10944     * @return {CompositeElement} this
10945     */
10946     removeElement : function(el, removeDom){
10947         if(el instanceof Array){
10948             for(var i = 0, len = el.length; i < len; i++){
10949                 this.removeElement(el[i]);
10950             }
10951             return this;
10952         }
10953         var index = typeof el == 'number' ? el : this.indexOf(el);
10954         if(index !== -1){
10955             if(removeDom){
10956                 var d = this.elements[index];
10957                 if(d.dom){
10958                     d.remove();
10959                 }else{
10960                     d.parentNode.removeChild(d);
10961                 }
10962             }
10963             this.elements.splice(index, 1);
10964         }
10965         return this;
10966     },
10967
10968     /**
10969     * Replaces the specified element with the passed element.
10970     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
10971     * to replace.
10972     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
10973     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
10974     * @return {CompositeElement} this
10975     */
10976     replaceElement : function(el, replacement, domReplace){
10977         var index = typeof el == 'number' ? el : this.indexOf(el);
10978         if(index !== -1){
10979             if(domReplace){
10980                 this.elements[index].replaceWith(replacement);
10981             }else{
10982                 this.elements.splice(index, 1, Roo.get(replacement))
10983             }
10984         }
10985         return this;
10986     },
10987
10988     /**
10989      * Removes all elements.
10990      */
10991     clear : function(){
10992         this.elements = [];
10993     }
10994 };
10995 (function(){
10996     Roo.CompositeElement.createCall = function(proto, fnName){
10997         if(!proto[fnName]){
10998             proto[fnName] = function(){
10999                 return this.invoke(fnName, arguments);
11000             };
11001         }
11002     };
11003     for(var fnName in Roo.Element.prototype){
11004         if(typeof Roo.Element.prototype[fnName] == "function"){
11005             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11006         }
11007     };
11008 })();
11009 /*
11010  * Based on:
11011  * Ext JS Library 1.1.1
11012  * Copyright(c) 2006-2007, Ext JS, LLC.
11013  *
11014  * Originally Released Under LGPL - original licence link has changed is not relivant.
11015  *
11016  * Fork - LGPL
11017  * <script type="text/javascript">
11018  */
11019
11020 /**
11021  * @class Roo.CompositeElementLite
11022  * @extends Roo.CompositeElement
11023  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11024  <pre><code>
11025  var els = Roo.select("#some-el div.some-class");
11026  // or select directly from an existing element
11027  var el = Roo.get('some-el');
11028  el.select('div.some-class');
11029
11030  els.setWidth(100); // all elements become 100 width
11031  els.hide(true); // all elements fade out and hide
11032  // or
11033  els.setWidth(100).hide(true);
11034  </code></pre><br><br>
11035  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11036  * actions will be performed on all the elements in this collection.</b>
11037  */
11038 Roo.CompositeElementLite = function(els){
11039     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11040     this.el = new Roo.Element.Flyweight();
11041 };
11042 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11043     addElements : function(els){
11044         if(els){
11045             if(els instanceof Array){
11046                 this.elements = this.elements.concat(els);
11047             }else{
11048                 var yels = this.elements;
11049                 var index = yels.length-1;
11050                 for(var i = 0, len = els.length; i < len; i++) {
11051                     yels[++index] = els[i];
11052                 }
11053             }
11054         }
11055         return this;
11056     },
11057     invoke : function(fn, args){
11058         var els = this.elements;
11059         var el = this.el;
11060         for(var i = 0, len = els.length; i < len; i++) {
11061             el.dom = els[i];
11062                 Roo.Element.prototype[fn].apply(el, args);
11063         }
11064         return this;
11065     },
11066     /**
11067      * Returns a flyweight Element of the dom element object at the specified index
11068      * @param {Number} index
11069      * @return {Roo.Element}
11070      */
11071     item : function(index){
11072         if(!this.elements[index]){
11073             return null;
11074         }
11075         this.el.dom = this.elements[index];
11076         return this.el;
11077     },
11078
11079     // fixes scope with flyweight
11080     addListener : function(eventName, handler, scope, opt){
11081         var els = this.elements;
11082         for(var i = 0, len = els.length; i < len; i++) {
11083             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11084         }
11085         return this;
11086     },
11087
11088     /**
11089     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11090     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11091     * a reference to the dom node, use el.dom.</b>
11092     * @param {Function} fn The function to call
11093     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11094     * @return {CompositeElement} this
11095     */
11096     each : function(fn, scope){
11097         var els = this.elements;
11098         var el = this.el;
11099         for(var i = 0, len = els.length; i < len; i++){
11100             el.dom = els[i];
11101                 if(fn.call(scope || el, el, this, i) === false){
11102                 break;
11103             }
11104         }
11105         return this;
11106     },
11107
11108     indexOf : function(el){
11109         return this.elements.indexOf(Roo.getDom(el));
11110     },
11111
11112     replaceElement : function(el, replacement, domReplace){
11113         var index = typeof el == 'number' ? el : this.indexOf(el);
11114         if(index !== -1){
11115             replacement = Roo.getDom(replacement);
11116             if(domReplace){
11117                 var d = this.elements[index];
11118                 d.parentNode.insertBefore(replacement, d);
11119                 d.parentNode.removeChild(d);
11120             }
11121             this.elements.splice(index, 1, replacement);
11122         }
11123         return this;
11124     }
11125 });
11126 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11127
11128 /*
11129  * Based on:
11130  * Ext JS Library 1.1.1
11131  * Copyright(c) 2006-2007, Ext JS, LLC.
11132  *
11133  * Originally Released Under LGPL - original licence link has changed is not relivant.
11134  *
11135  * Fork - LGPL
11136  * <script type="text/javascript">
11137  */
11138
11139  
11140
11141 /**
11142  * @class Roo.data.Connection
11143  * @extends Roo.util.Observable
11144  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11145  * either to a configured URL, or to a URL specified at request time.<br><br>
11146  * <p>
11147  * Requests made by this class are asynchronous, and will return immediately. No data from
11148  * the server will be available to the statement immediately following the {@link #request} call.
11149  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
11150  * <p>
11151  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11152  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11153  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11154  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
11155  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11156  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11157  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11158  * standard DOM methods.
11159  * @constructor
11160  * @param {Object} config a configuration object.
11161  */
11162 Roo.data.Connection = function(config){
11163     Roo.apply(this, config);
11164     this.addEvents({
11165         /**
11166          * @event beforerequest
11167          * Fires before a network request is made to retrieve a data object.
11168          * @param {Connection} conn This Connection object.
11169          * @param {Object} options The options config object passed to the {@link #request} method.
11170          */
11171         "beforerequest" : true,
11172         /**
11173          * @event requestcomplete
11174          * Fires if the request was successfully completed.
11175          * @param {Connection} conn This Connection object.
11176          * @param {Object} response The XHR object containing the response data.
11177          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11178          * @param {Object} options The options config object passed to the {@link #request} method.
11179          */
11180         "requestcomplete" : true,
11181         /**
11182          * @event requestexception
11183          * Fires if an error HTTP status was returned from the server.
11184          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11185          * @param {Connection} conn This Connection object.
11186          * @param {Object} response The XHR object containing the response data.
11187          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11188          * @param {Object} options The options config object passed to the {@link #request} method.
11189          */
11190         "requestexception" : true
11191     });
11192     Roo.data.Connection.superclass.constructor.call(this);
11193 };
11194
11195 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11196     /**
11197      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11198      */
11199     /**
11200      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11201      * extra parameters to each request made by this object. (defaults to undefined)
11202      */
11203     /**
11204      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11205      *  to each request made by this object. (defaults to undefined)
11206      */
11207     /**
11208      * @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)
11209      */
11210     /**
11211      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11212      */
11213     timeout : 30000,
11214     /**
11215      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11216      * @type Boolean
11217      */
11218     autoAbort:false,
11219
11220     /**
11221      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11222      * @type Boolean
11223      */
11224     disableCaching: true,
11225
11226     /**
11227      * Sends an HTTP request to a remote server.
11228      * @param {Object} options An object which may contain the following properties:<ul>
11229      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11230      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11231      * request, a url encoded string or a function to call to get either.</li>
11232      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11233      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11234      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11235      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11236      * <li>options {Object} The parameter to the request call.</li>
11237      * <li>success {Boolean} True if the request succeeded.</li>
11238      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11239      * </ul></li>
11240      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11241      * The callback is passed the following parameters:<ul>
11242      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11243      * <li>options {Object} The parameter to the request call.</li>
11244      * </ul></li>
11245      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11246      * The callback is passed the following parameters:<ul>
11247      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11248      * <li>options {Object} The parameter to the request call.</li>
11249      * </ul></li>
11250      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11251      * for the callback function. Defaults to the browser window.</li>
11252      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11253      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11254      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11255      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11256      * params for the post data. Any params will be appended to the URL.</li>
11257      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11258      * </ul>
11259      * @return {Number} transactionId
11260      */
11261     request : function(o){
11262         if(this.fireEvent("beforerequest", this, o) !== false){
11263             var p = o.params;
11264
11265             if(typeof p == "function"){
11266                 p = p.call(o.scope||window, o);
11267             }
11268             if(typeof p == "object"){
11269                 p = Roo.urlEncode(o.params);
11270             }
11271             if(this.extraParams){
11272                 var extras = Roo.urlEncode(this.extraParams);
11273                 p = p ? (p + '&' + extras) : extras;
11274             }
11275
11276             var url = o.url || this.url;
11277             if(typeof url == 'function'){
11278                 url = url.call(o.scope||window, o);
11279             }
11280
11281             if(o.form){
11282                 var form = Roo.getDom(o.form);
11283                 url = url || form.action;
11284
11285                 var enctype = form.getAttribute("enctype");
11286                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11287                     return this.doFormUpload(o, p, url);
11288                 }
11289                 var f = Roo.lib.Ajax.serializeForm(form);
11290                 p = p ? (p + '&' + f) : f;
11291             }
11292
11293             var hs = o.headers;
11294             if(this.defaultHeaders){
11295                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11296                 if(!o.headers){
11297                     o.headers = hs;
11298                 }
11299             }
11300
11301             var cb = {
11302                 success: this.handleResponse,
11303                 failure: this.handleFailure,
11304                 scope: this,
11305                 argument: {options: o},
11306                 timeout : this.timeout
11307             };
11308
11309             var method = o.method||this.method||(p ? "POST" : "GET");
11310
11311             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11312                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11313             }
11314
11315             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11316                 if(o.autoAbort){
11317                     this.abort();
11318                 }
11319             }else if(this.autoAbort !== false){
11320                 this.abort();
11321             }
11322
11323             if((method == 'GET' && p) || o.xmlData){
11324                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11325                 p = '';
11326             }
11327             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11328             return this.transId;
11329         }else{
11330             Roo.callback(o.callback, o.scope, [o, null, null]);
11331             return null;
11332         }
11333     },
11334
11335     /**
11336      * Determine whether this object has a request outstanding.
11337      * @param {Number} transactionId (Optional) defaults to the last transaction
11338      * @return {Boolean} True if there is an outstanding request.
11339      */
11340     isLoading : function(transId){
11341         if(transId){
11342             return Roo.lib.Ajax.isCallInProgress(transId);
11343         }else{
11344             return this.transId ? true : false;
11345         }
11346     },
11347
11348     /**
11349      * Aborts any outstanding request.
11350      * @param {Number} transactionId (Optional) defaults to the last transaction
11351      */
11352     abort : function(transId){
11353         if(transId || this.isLoading()){
11354             Roo.lib.Ajax.abort(transId || this.transId);
11355         }
11356     },
11357
11358     // private
11359     handleResponse : function(response){
11360         this.transId = false;
11361         var options = response.argument.options;
11362         response.argument = options ? options.argument : null;
11363         this.fireEvent("requestcomplete", this, response, options);
11364         Roo.callback(options.success, options.scope, [response, options]);
11365         Roo.callback(options.callback, options.scope, [options, true, response]);
11366     },
11367
11368     // private
11369     handleFailure : function(response, e){
11370         this.transId = false;
11371         var options = response.argument.options;
11372         response.argument = options ? options.argument : null;
11373         this.fireEvent("requestexception", this, response, options, e);
11374         Roo.callback(options.failure, options.scope, [response, options]);
11375         Roo.callback(options.callback, options.scope, [options, false, response]);
11376     },
11377
11378     // private
11379     doFormUpload : function(o, ps, url){
11380         var id = Roo.id();
11381         var frame = document.createElement('iframe');
11382         frame.id = id;
11383         frame.name = id;
11384         frame.className = 'x-hidden';
11385         if(Roo.isIE){
11386             frame.src = Roo.SSL_SECURE_URL;
11387         }
11388         document.body.appendChild(frame);
11389
11390         if(Roo.isIE){
11391            document.frames[id].name = id;
11392         }
11393
11394         var form = Roo.getDom(o.form);
11395         form.target = id;
11396         form.method = 'POST';
11397         form.enctype = form.encoding = 'multipart/form-data';
11398         if(url){
11399             form.action = url;
11400         }
11401
11402         var hiddens, hd;
11403         if(ps){ // add dynamic params
11404             hiddens = [];
11405             ps = Roo.urlDecode(ps, false);
11406             for(var k in ps){
11407                 if(ps.hasOwnProperty(k)){
11408                     hd = document.createElement('input');
11409                     hd.type = 'hidden';
11410                     hd.name = k;
11411                     hd.value = ps[k];
11412                     form.appendChild(hd);
11413                     hiddens.push(hd);
11414                 }
11415             }
11416         }
11417
11418         function cb(){
11419             var r = {  // bogus response object
11420                 responseText : '',
11421                 responseXML : null
11422             };
11423
11424             r.argument = o ? o.argument : null;
11425
11426             try { //
11427                 var doc;
11428                 if(Roo.isIE){
11429                     doc = frame.contentWindow.document;
11430                 }else {
11431                     doc = (frame.contentDocument || window.frames[id].document);
11432                 }
11433                 if(doc && doc.body){
11434                     r.responseText = doc.body.innerHTML;
11435                 }
11436                 if(doc && doc.XMLDocument){
11437                     r.responseXML = doc.XMLDocument;
11438                 }else {
11439                     r.responseXML = doc;
11440                 }
11441             }
11442             catch(e) {
11443                 // ignore
11444             }
11445
11446             Roo.EventManager.removeListener(frame, 'load', cb, this);
11447
11448             this.fireEvent("requestcomplete", this, r, o);
11449             Roo.callback(o.success, o.scope, [r, o]);
11450             Roo.callback(o.callback, o.scope, [o, true, r]);
11451
11452             setTimeout(function(){document.body.removeChild(frame);}, 100);
11453         }
11454
11455         Roo.EventManager.on(frame, 'load', cb, this);
11456         form.submit();
11457
11458         if(hiddens){ // remove dynamic params
11459             for(var i = 0, len = hiddens.length; i < len; i++){
11460                 form.removeChild(hiddens[i]);
11461             }
11462         }
11463     }
11464 });
11465
11466 /**
11467  * @class Roo.Ajax
11468  * @extends Roo.data.Connection
11469  * Global Ajax request class.
11470  *
11471  * @singleton
11472  */
11473 Roo.Ajax = new Roo.data.Connection({
11474     // fix up the docs
11475    /**
11476      * @cfg {String} url @hide
11477      */
11478     /**
11479      * @cfg {Object} extraParams @hide
11480      */
11481     /**
11482      * @cfg {Object} defaultHeaders @hide
11483      */
11484     /**
11485      * @cfg {String} method (Optional) @hide
11486      */
11487     /**
11488      * @cfg {Number} timeout (Optional) @hide
11489      */
11490     /**
11491      * @cfg {Boolean} autoAbort (Optional) @hide
11492      */
11493
11494     /**
11495      * @cfg {Boolean} disableCaching (Optional) @hide
11496      */
11497
11498     /**
11499      * @property  disableCaching
11500      * True to add a unique cache-buster param to GET requests. (defaults to true)
11501      * @type Boolean
11502      */
11503     /**
11504      * @property  url
11505      * The default URL to be used for requests to the server. (defaults to undefined)
11506      * @type String
11507      */
11508     /**
11509      * @property  extraParams
11510      * An object containing properties which are used as
11511      * extra parameters to each request made by this object. (defaults to undefined)
11512      * @type Object
11513      */
11514     /**
11515      * @property  defaultHeaders
11516      * An object containing request headers which are added to each request made by this object. (defaults to undefined)
11517      * @type Object
11518      */
11519     /**
11520      * @property  method
11521      * The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11522      * @type String
11523      */
11524     /**
11525      * @property  timeout
11526      * The timeout in milliseconds to be used for requests. (defaults to 30000)
11527      * @type Number
11528      */
11529
11530     /**
11531      * @property  autoAbort
11532      * Whether a new request should abort any pending requests. (defaults to false)
11533      * @type Boolean
11534      */
11535     autoAbort : false,
11536
11537     /**
11538      * Serialize the passed form into a url encoded string
11539      * @param {String/HTMLElement} form
11540      * @return {String}
11541      */
11542     serializeForm : function(form){
11543         return Roo.lib.Ajax.serializeForm(form);
11544     }
11545 });/*
11546  * Based on:
11547  * Ext JS Library 1.1.1
11548  * Copyright(c) 2006-2007, Ext JS, LLC.
11549  *
11550  * Originally Released Under LGPL - original licence link has changed is not relivant.
11551  *
11552  * Fork - LGPL
11553  * <script type="text/javascript">
11554  */
11555  
11556 /**
11557  * @class Roo.Ajax
11558  * @extends Roo.data.Connection
11559  * Global Ajax request class.
11560  *
11561  * @instanceOf  Roo.data.Connection
11562  */
11563 Roo.Ajax = new Roo.data.Connection({
11564     // fix up the docs
11565     
11566     /**
11567      * fix up scoping
11568      * @scope Roo.Ajax
11569      */
11570     
11571    /**
11572      * @cfg {String} url @hide
11573      */
11574     /**
11575      * @cfg {Object} extraParams @hide
11576      */
11577     /**
11578      * @cfg {Object} defaultHeaders @hide
11579      */
11580     /**
11581      * @cfg {String} method (Optional) @hide
11582      */
11583     /**
11584      * @cfg {Number} timeout (Optional) @hide
11585      */
11586     /**
11587      * @cfg {Boolean} autoAbort (Optional) @hide
11588      */
11589
11590     /**
11591      * @cfg {Boolean} disableCaching (Optional) @hide
11592      */
11593
11594     /**
11595      * @property  disableCaching
11596      * True to add a unique cache-buster param to GET requests. (defaults to true)
11597      * @type Boolean
11598      */
11599     /**
11600      * @property  url
11601      * The default URL to be used for requests to the server. (defaults to undefined)
11602      * @type String
11603      */
11604     /**
11605      * @property  extraParams
11606      * An object containing properties which are used as
11607      * extra parameters to each request made by this object. (defaults to undefined)
11608      * @type Object
11609      */
11610     /**
11611      * @property  defaultHeaders
11612      * An object containing request headers which are added to each request made by this object. (defaults to undefined)
11613      * @type Object
11614      */
11615     /**
11616      * @property  method
11617      * The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11618      * @type String
11619      */
11620     /**
11621      * @property  timeout
11622      * The timeout in milliseconds to be used for requests. (defaults to 30000)
11623      * @type Number
11624      */
11625
11626     /**
11627      * @property  autoAbort
11628      * Whether a new request should abort any pending requests. (defaults to false)
11629      * @type Boolean
11630      */
11631     autoAbort : false,
11632
11633     /**
11634      * Serialize the passed form into a url encoded string
11635      * @param {String/HTMLElement} form
11636      * @return {String}
11637      */
11638     serializeForm : function(form){
11639         return Roo.lib.Ajax.serializeForm(form);
11640     }
11641 });/*
11642  * Based on:
11643  * Ext JS Library 1.1.1
11644  * Copyright(c) 2006-2007, Ext JS, LLC.
11645  *
11646  * Originally Released Under LGPL - original licence link has changed is not relivant.
11647  *
11648  * Fork - LGPL
11649  * <script type="text/javascript">
11650  */
11651
11652  
11653 /**
11654  * @class Roo.UpdateManager
11655  * @extends Roo.util.Observable
11656  * Provides AJAX-style update for Element object.<br><br>
11657  * Usage:<br>
11658  * <pre><code>
11659  * // Get it from a Roo.Element object
11660  * var el = Roo.get("foo");
11661  * var mgr = el.getUpdateManager();
11662  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11663  * ...
11664  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11665  * <br>
11666  * // or directly (returns the same UpdateManager instance)
11667  * var mgr = new Roo.UpdateManager("myElementId");
11668  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11669  * mgr.on("update", myFcnNeedsToKnow);
11670  * <br>
11671    // short handed call directly from the element object
11672    Roo.get("foo").load({
11673         url: "bar.php",
11674         scripts:true,
11675         params: "for=bar",
11676         text: "Loading Foo..."
11677    });
11678  * </code></pre>
11679  * @constructor
11680  * Create new UpdateManager directly.
11681  * @param {String/HTMLElement/Roo.Element} el The element to update
11682  * @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).
11683  */
11684 Roo.UpdateManager = function(el, forceNew){
11685     el = Roo.get(el);
11686     if(!forceNew && el.updateManager){
11687         return el.updateManager;
11688     }
11689     /**
11690      * The Element object
11691      * @type Roo.Element
11692      */
11693     this.el = el;
11694     /**
11695      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11696      * @type String
11697      */
11698     this.defaultUrl = null;
11699
11700     this.addEvents({
11701         /**
11702          * @event beforeupdate
11703          * Fired before an update is made, return false from your handler and the update is cancelled.
11704          * @param {Roo.Element} el
11705          * @param {String/Object/Function} url
11706          * @param {String/Object} params
11707          */
11708         "beforeupdate": true,
11709         /**
11710          * @event update
11711          * Fired after successful update is made.
11712          * @param {Roo.Element} el
11713          * @param {Object} oResponseObject The response Object
11714          */
11715         "update": true,
11716         /**
11717          * @event failure
11718          * Fired on update failure.
11719          * @param {Roo.Element} el
11720          * @param {Object} oResponseObject The response Object
11721          */
11722         "failure": true
11723     });
11724     var d = Roo.UpdateManager.defaults;
11725     /**
11726      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11727      * @type String
11728      */
11729     this.sslBlankUrl = d.sslBlankUrl;
11730     /**
11731      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11732      * @type Boolean
11733      */
11734     this.disableCaching = d.disableCaching;
11735     /**
11736      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11737      * @type String
11738      */
11739     this.indicatorText = d.indicatorText;
11740     /**
11741      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11742      * @type String
11743      */
11744     this.showLoadIndicator = d.showLoadIndicator;
11745     /**
11746      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11747      * @type Number
11748      */
11749     this.timeout = d.timeout;
11750
11751     /**
11752      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11753      * @type Boolean
11754      */
11755     this.loadScripts = d.loadScripts;
11756
11757     /**
11758      * Transaction object of current executing transaction
11759      */
11760     this.transaction = null;
11761
11762     /**
11763      * @private
11764      */
11765     this.autoRefreshProcId = null;
11766     /**
11767      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11768      * @type Function
11769      */
11770     this.refreshDelegate = this.refresh.createDelegate(this);
11771     /**
11772      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11773      * @type Function
11774      */
11775     this.updateDelegate = this.update.createDelegate(this);
11776     /**
11777      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11778      * @type Function
11779      */
11780     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11781     /**
11782      * @private
11783      */
11784     this.successDelegate = this.processSuccess.createDelegate(this);
11785     /**
11786      * @private
11787      */
11788     this.failureDelegate = this.processFailure.createDelegate(this);
11789
11790     if(!this.renderer){
11791      /**
11792       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11793       */
11794     this.renderer = new Roo.UpdateManager.BasicRenderer();
11795     }
11796     
11797     Roo.UpdateManager.superclass.constructor.call(this);
11798 };
11799
11800 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11801     /**
11802      * Get the Element this UpdateManager is bound to
11803      * @return {Roo.Element} The element
11804      */
11805     getEl : function(){
11806         return this.el;
11807     },
11808     /**
11809      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11810      * @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:
11811 <pre><code>
11812 um.update({<br/>
11813     url: "your-url.php",<br/>
11814     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11815     callback: yourFunction,<br/>
11816     scope: yourObject, //(optional scope)  <br/>
11817     discardUrl: false, <br/>
11818     nocache: false,<br/>
11819     text: "Loading...",<br/>
11820     timeout: 30,<br/>
11821     scripts: false<br/>
11822 });
11823 </code></pre>
11824      * The only required property is url. The optional properties nocache, text and scripts
11825      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11826      * @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}
11827      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11828      * @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.
11829      */
11830     update : function(url, params, callback, discardUrl){
11831         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11832             var method = this.method, cfg;
11833             if(typeof url == "object"){ // must be config object
11834                 cfg = url;
11835                 url = cfg.url;
11836                 params = params || cfg.params;
11837                 callback = callback || cfg.callback;
11838                 discardUrl = discardUrl || cfg.discardUrl;
11839                 if(callback && cfg.scope){
11840                     callback = callback.createDelegate(cfg.scope);
11841                 }
11842                 if(typeof cfg.method != "undefined"){method = cfg.method;};
11843                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
11844                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
11845                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
11846                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
11847             }
11848             this.showLoading();
11849             if(!discardUrl){
11850                 this.defaultUrl = url;
11851             }
11852             if(typeof url == "function"){
11853                 url = url.call(this);
11854             }
11855
11856             method = method || (params ? "POST" : "GET");
11857             if(method == "GET"){
11858                 url = this.prepareUrl(url);
11859             }
11860
11861             var o = Roo.apply(cfg ||{}, {
11862                 url : url,
11863                 params: params,
11864                 success: this.successDelegate,
11865                 failure: this.failureDelegate,
11866                 callback: undefined,
11867                 timeout: (this.timeout*1000),
11868                 argument: {"url": url, "form": null, "callback": callback, "params": params}
11869             });
11870
11871             this.transaction = Roo.Ajax.request(o);
11872         }
11873     },
11874
11875     /**
11876      * 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.
11877      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
11878      * @param {String/HTMLElement} form The form Id or form element
11879      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
11880      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
11881      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11882      */
11883     formUpdate : function(form, url, reset, callback){
11884         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
11885             if(typeof url == "function"){
11886                 url = url.call(this);
11887             }
11888             form = Roo.getDom(form);
11889             this.transaction = Roo.Ajax.request({
11890                 form: form,
11891                 url:url,
11892                 success: this.successDelegate,
11893                 failure: this.failureDelegate,
11894                 timeout: (this.timeout*1000),
11895                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
11896             });
11897             this.showLoading.defer(1, this);
11898         }
11899     },
11900
11901     /**
11902      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
11903      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11904      */
11905     refresh : function(callback){
11906         if(this.defaultUrl == null){
11907             return;
11908         }
11909         this.update(this.defaultUrl, null, callback, true);
11910     },
11911
11912     /**
11913      * Set this element to auto refresh.
11914      * @param {Number} interval How often to update (in seconds).
11915      * @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)
11916      * @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}
11917      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11918      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
11919      */
11920     startAutoRefresh : function(interval, url, params, callback, refreshNow){
11921         if(refreshNow){
11922             this.update(url || this.defaultUrl, params, callback, true);
11923         }
11924         if(this.autoRefreshProcId){
11925             clearInterval(this.autoRefreshProcId);
11926         }
11927         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
11928     },
11929
11930     /**
11931      * Stop auto refresh on this element.
11932      */
11933      stopAutoRefresh : function(){
11934         if(this.autoRefreshProcId){
11935             clearInterval(this.autoRefreshProcId);
11936             delete this.autoRefreshProcId;
11937         }
11938     },
11939
11940     isAutoRefreshing : function(){
11941        return this.autoRefreshProcId ? true : false;
11942     },
11943     /**
11944      * Called to update the element to "Loading" state. Override to perform custom action.
11945      */
11946     showLoading : function(){
11947         if(this.showLoadIndicator){
11948             this.el.update(this.indicatorText);
11949         }
11950     },
11951
11952     /**
11953      * Adds unique parameter to query string if disableCaching = true
11954      * @private
11955      */
11956     prepareUrl : function(url){
11957         if(this.disableCaching){
11958             var append = "_dc=" + (new Date().getTime());
11959             if(url.indexOf("?") !== -1){
11960                 url += "&" + append;
11961             }else{
11962                 url += "?" + append;
11963             }
11964         }
11965         return url;
11966     },
11967
11968     /**
11969      * @private
11970      */
11971     processSuccess : function(response){
11972         this.transaction = null;
11973         if(response.argument.form && response.argument.reset){
11974             try{ // put in try/catch since some older FF releases had problems with this
11975                 response.argument.form.reset();
11976             }catch(e){}
11977         }
11978         if(this.loadScripts){
11979             this.renderer.render(this.el, response, this,
11980                 this.updateComplete.createDelegate(this, [response]));
11981         }else{
11982             this.renderer.render(this.el, response, this);
11983             this.updateComplete(response);
11984         }
11985     },
11986
11987     updateComplete : function(response){
11988         this.fireEvent("update", this.el, response);
11989         if(typeof response.argument.callback == "function"){
11990             response.argument.callback(this.el, true, response);
11991         }
11992     },
11993
11994     /**
11995      * @private
11996      */
11997     processFailure : function(response){
11998         this.transaction = null;
11999         this.fireEvent("failure", this.el, response);
12000         if(typeof response.argument.callback == "function"){
12001             response.argument.callback(this.el, false, response);
12002         }
12003     },
12004
12005     /**
12006      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
12007      * @param {Object} renderer The object implementing the render() method
12008      */
12009     setRenderer : function(renderer){
12010         this.renderer = renderer;
12011     },
12012
12013     getRenderer : function(){
12014        return this.renderer;
12015     },
12016
12017     /**
12018      * Set the defaultUrl used for updates
12019      * @param {String/Function} defaultUrl The url or a function to call to get the url
12020      */
12021     setDefaultUrl : function(defaultUrl){
12022         this.defaultUrl = defaultUrl;
12023     },
12024
12025     /**
12026      * Aborts the executing transaction
12027      */
12028     abort : function(){
12029         if(this.transaction){
12030             Roo.Ajax.abort(this.transaction);
12031         }
12032     },
12033
12034     /**
12035      * Returns true if an update is in progress
12036      * @return {Boolean}
12037      */
12038     isUpdating : function(){
12039         if(this.transaction){
12040             return Roo.Ajax.isLoading(this.transaction);
12041         }
12042         return false;
12043     }
12044 });
12045
12046 /**
12047  * @class Roo.UpdateManager.defaults
12048  * @static (not really - but it helps the doc tool)
12049  * The defaults collection enables customizing the default properties of UpdateManager
12050  */
12051    Roo.UpdateManager.defaults = {
12052        /**
12053          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12054          * @type Number
12055          */
12056          timeout : 30,
12057
12058          /**
12059          * True to process scripts by default (Defaults to false).
12060          * @type Boolean
12061          */
12062         loadScripts : false,
12063
12064         /**
12065         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12066         * @type String
12067         */
12068         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12069         /**
12070          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12071          * @type Boolean
12072          */
12073         disableCaching : false,
12074         /**
12075          * Whether to show indicatorText when loading (Defaults to true).
12076          * @type Boolean
12077          */
12078         showLoadIndicator : true,
12079         /**
12080          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12081          * @type String
12082          */
12083         indicatorText : '<div class="loading-indicator">Loading...</div>'
12084    };
12085
12086 /**
12087  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12088  *Usage:
12089  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12090  * @param {String/HTMLElement/Roo.Element} el The element to update
12091  * @param {String} url The url
12092  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12093  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12094  * @static
12095  * @deprecated
12096  * @member Roo.UpdateManager
12097  */
12098 Roo.UpdateManager.updateElement = function(el, url, params, options){
12099     var um = Roo.get(el, true).getUpdateManager();
12100     Roo.apply(um, options);
12101     um.update(url, params, options ? options.callback : null);
12102 };
12103 // alias for backwards compat
12104 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12105 /**
12106  * @class Roo.UpdateManager.BasicRenderer
12107  * Default Content renderer. Updates the elements innerHTML with the responseText.
12108  */
12109 Roo.UpdateManager.BasicRenderer = function(){};
12110
12111 Roo.UpdateManager.BasicRenderer.prototype = {
12112     /**
12113      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12114      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12115      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12116      * @param {Roo.Element} el The element being rendered
12117      * @param {Object} response The YUI Connect response object
12118      * @param {UpdateManager} updateManager The calling update manager
12119      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12120      */
12121      render : function(el, response, updateManager, callback){
12122         el.update(response.responseText, updateManager.loadScripts, callback);
12123     }
12124 };
12125 /*
12126  * Based on:
12127  * Ext JS Library 1.1.1
12128  * Copyright(c) 2006-2007, Ext JS, LLC.
12129  *
12130  * Originally Released Under LGPL - original licence link has changed is not relivant.
12131  *
12132  * Fork - LGPL
12133  * <script type="text/javascript">
12134  */
12135
12136 /**
12137  * @class Roo.util.DelayedTask
12138  * Provides a convenient method of performing setTimeout where a new
12139  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12140  * You can use this class to buffer
12141  * the keypress events for a certain number of milliseconds, and perform only if they stop
12142  * for that amount of time.
12143  * @constructor The parameters to this constructor serve as defaults and are not required.
12144  * @param {Function} fn (optional) The default function to timeout
12145  * @param {Object} scope (optional) The default scope of that timeout
12146  * @param {Array} args (optional) The default Array of arguments
12147  */
12148 Roo.util.DelayedTask = function(fn, scope, args){
12149     var id = null, d, t;
12150
12151     var call = function(){
12152         var now = new Date().getTime();
12153         if(now - t >= d){
12154             clearInterval(id);
12155             id = null;
12156             fn.apply(scope, args || []);
12157         }
12158     };
12159     /**
12160      * Cancels any pending timeout and queues a new one
12161      * @param {Number} delay The milliseconds to delay
12162      * @param {Function} newFn (optional) Overrides function passed to constructor
12163      * @param {Object} newScope (optional) Overrides scope passed to constructor
12164      * @param {Array} newArgs (optional) Overrides args passed to constructor
12165      */
12166     this.delay = function(delay, newFn, newScope, newArgs){
12167         if(id && delay != d){
12168             this.cancel();
12169         }
12170         d = delay;
12171         t = new Date().getTime();
12172         fn = newFn || fn;
12173         scope = newScope || scope;
12174         args = newArgs || args;
12175         if(!id){
12176             id = setInterval(call, d);
12177         }
12178     };
12179
12180     /**
12181      * Cancel the last queued timeout
12182      */
12183     this.cancel = function(){
12184         if(id){
12185             clearInterval(id);
12186             id = null;
12187         }
12188     };
12189 };/*
12190  * Based on:
12191  * Ext JS Library 1.1.1
12192  * Copyright(c) 2006-2007, Ext JS, LLC.
12193  *
12194  * Originally Released Under LGPL - original licence link has changed is not relivant.
12195  *
12196  * Fork - LGPL
12197  * <script type="text/javascript">
12198  */
12199  
12200  
12201 Roo.util.TaskRunner = function(interval){
12202     interval = interval || 10;
12203     var tasks = [], removeQueue = [];
12204     var id = 0;
12205     var running = false;
12206
12207     var stopThread = function(){
12208         running = false;
12209         clearInterval(id);
12210         id = 0;
12211     };
12212
12213     var startThread = function(){
12214         if(!running){
12215             running = true;
12216             id = setInterval(runTasks, interval);
12217         }
12218     };
12219
12220     var removeTask = function(task){
12221         removeQueue.push(task);
12222         if(task.onStop){
12223             task.onStop();
12224         }
12225     };
12226
12227     var runTasks = function(){
12228         if(removeQueue.length > 0){
12229             for(var i = 0, len = removeQueue.length; i < len; i++){
12230                 tasks.remove(removeQueue[i]);
12231             }
12232             removeQueue = [];
12233             if(tasks.length < 1){
12234                 stopThread();
12235                 return;
12236             }
12237         }
12238         var now = new Date().getTime();
12239         for(var i = 0, len = tasks.length; i < len; ++i){
12240             var t = tasks[i];
12241             var itime = now - t.taskRunTime;
12242             if(t.interval <= itime){
12243                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12244                 t.taskRunTime = now;
12245                 if(rt === false || t.taskRunCount === t.repeat){
12246                     removeTask(t);
12247                     return;
12248                 }
12249             }
12250             if(t.duration && t.duration <= (now - t.taskStartTime)){
12251                 removeTask(t);
12252             }
12253         }
12254     };
12255
12256     /**
12257      * Queues a new task.
12258      * @param {Object} task
12259      */
12260     this.start = function(task){
12261         tasks.push(task);
12262         task.taskStartTime = new Date().getTime();
12263         task.taskRunTime = 0;
12264         task.taskRunCount = 0;
12265         startThread();
12266         return task;
12267     };
12268
12269     this.stop = function(task){
12270         removeTask(task);
12271         return task;
12272     };
12273
12274     this.stopAll = function(){
12275         stopThread();
12276         for(var i = 0, len = tasks.length; i < len; i++){
12277             if(tasks[i].onStop){
12278                 tasks[i].onStop();
12279             }
12280         }
12281         tasks = [];
12282         removeQueue = [];
12283     };
12284 };
12285
12286 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12287  * Based on:
12288  * Ext JS Library 1.1.1
12289  * Copyright(c) 2006-2007, Ext JS, LLC.
12290  *
12291  * Originally Released Under LGPL - original licence link has changed is not relivant.
12292  *
12293  * Fork - LGPL
12294  * <script type="text/javascript">
12295  */
12296
12297  
12298 /**
12299  * @class Roo.util.MixedCollection
12300  * @extends Roo.util.Observable
12301  * A Collection class that maintains both numeric indexes and keys and exposes events.
12302  * @constructor
12303  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12304  * collection (defaults to false)
12305  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12306  * and return the key value for that item.  This is used when available to look up the key on items that
12307  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12308  * equivalent to providing an implementation for the {@link #getKey} method.
12309  */
12310 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12311     this.items = [];
12312     this.map = {};
12313     this.keys = [];
12314     this.length = 0;
12315     this.addEvents({
12316         /**
12317          * @event clear
12318          * Fires when the collection is cleared.
12319          */
12320         "clear" : true,
12321         /**
12322          * @event add
12323          * Fires when an item is added to the collection.
12324          * @param {Number} index The index at which the item was added.
12325          * @param {Object} o The item added.
12326          * @param {String} key The key associated with the added item.
12327          */
12328         "add" : true,
12329         /**
12330          * @event replace
12331          * Fires when an item is replaced in the collection.
12332          * @param {String} key he key associated with the new added.
12333          * @param {Object} old The item being replaced.
12334          * @param {Object} new The new item.
12335          */
12336         "replace" : true,
12337         /**
12338          * @event remove
12339          * Fires when an item is removed from the collection.
12340          * @param {Object} o The item being removed.
12341          * @param {String} key (optional) The key associated with the removed item.
12342          */
12343         "remove" : true,
12344         "sort" : true
12345     });
12346     this.allowFunctions = allowFunctions === true;
12347     if(keyFn){
12348         this.getKey = keyFn;
12349     }
12350     Roo.util.MixedCollection.superclass.constructor.call(this);
12351 };
12352
12353 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12354     allowFunctions : false,
12355     
12356 /**
12357  * Adds an item to the collection.
12358  * @param {String} key The key to associate with the item
12359  * @param {Object} o The item to add.
12360  * @return {Object} The item added.
12361  */
12362     add : function(key, o){
12363         if(arguments.length == 1){
12364             o = arguments[0];
12365             key = this.getKey(o);
12366         }
12367         if(typeof key == "undefined" || key === null){
12368             this.length++;
12369             this.items.push(o);
12370             this.keys.push(null);
12371         }else{
12372             var old = this.map[key];
12373             if(old){
12374                 return this.replace(key, o);
12375             }
12376             this.length++;
12377             this.items.push(o);
12378             this.map[key] = o;
12379             this.keys.push(key);
12380         }
12381         this.fireEvent("add", this.length-1, o, key);
12382         return o;
12383     },
12384        
12385 /**
12386   * MixedCollection has a generic way to fetch keys if you implement getKey.
12387 <pre><code>
12388 // normal way
12389 var mc = new Roo.util.MixedCollection();
12390 mc.add(someEl.dom.id, someEl);
12391 mc.add(otherEl.dom.id, otherEl);
12392 //and so on
12393
12394 // using getKey
12395 var mc = new Roo.util.MixedCollection();
12396 mc.getKey = function(el){
12397    return el.dom.id;
12398 };
12399 mc.add(someEl);
12400 mc.add(otherEl);
12401
12402 // or via the constructor
12403 var mc = new Roo.util.MixedCollection(false, function(el){
12404    return el.dom.id;
12405 });
12406 mc.add(someEl);
12407 mc.add(otherEl);
12408 </code></pre>
12409  * @param o {Object} The item for which to find the key.
12410  * @return {Object} The key for the passed item.
12411  */
12412     getKey : function(o){
12413          return o.id; 
12414     },
12415    
12416 /**
12417  * Replaces an item in the collection.
12418  * @param {String} key The key associated with the item to replace, or the item to replace.
12419  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
12420  * @return {Object}  The new item.
12421  */
12422     replace : function(key, o){
12423         if(arguments.length == 1){
12424             o = arguments[0];
12425             key = this.getKey(o);
12426         }
12427         var old = this.item(key);
12428         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
12429              return this.add(key, o);
12430         }
12431         var index = this.indexOfKey(key);
12432         this.items[index] = o;
12433         this.map[key] = o;
12434         this.fireEvent("replace", key, old, o);
12435         return o;
12436     },
12437    
12438 /**
12439  * Adds all elements of an Array or an Object to the collection.
12440  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
12441  * an Array of values, each of which are added to the collection.
12442  */
12443     addAll : function(objs){
12444         if(arguments.length > 1 || objs instanceof Array){
12445             var args = arguments.length > 1 ? arguments : objs;
12446             for(var i = 0, len = args.length; i < len; i++){
12447                 this.add(args[i]);
12448             }
12449         }else{
12450             for(var key in objs){
12451                 if(this.allowFunctions || typeof objs[key] != "function"){
12452                     this.add(key, objs[key]);
12453                 }
12454             }
12455         }
12456     },
12457    
12458 /**
12459  * Executes the specified function once for every item in the collection, passing each
12460  * item as the first and only parameter. returning false from the function will stop the iteration.
12461  * @param {Function} fn The function to execute for each item.
12462  * @param {Object} scope (optional) The scope in which to execute the function.
12463  */
12464     each : function(fn, scope){
12465         var items = [].concat(this.items); // each safe for removal
12466         for(var i = 0, len = items.length; i < len; i++){
12467             if(fn.call(scope || items[i], items[i], i, len) === false){
12468                 break;
12469             }
12470         }
12471     },
12472    
12473 /**
12474  * Executes the specified function once for every key in the collection, passing each
12475  * key, and its associated item as the first two parameters.
12476  * @param {Function} fn The function to execute for each item.
12477  * @param {Object} scope (optional) The scope in which to execute the function.
12478  */
12479     eachKey : function(fn, scope){
12480         for(var i = 0, len = this.keys.length; i < len; i++){
12481             fn.call(scope || window, this.keys[i], this.items[i], i, len);
12482         }
12483     },
12484    
12485 /**
12486  * Returns the first item in the collection which elicits a true return value from the
12487  * passed selection function.
12488  * @param {Function} fn The selection function to execute for each item.
12489  * @param {Object} scope (optional) The scope in which to execute the function.
12490  * @return {Object} The first item in the collection which returned true from the selection function.
12491  */
12492     find : function(fn, scope){
12493         for(var i = 0, len = this.items.length; i < len; i++){
12494             if(fn.call(scope || window, this.items[i], this.keys[i])){
12495                 return this.items[i];
12496             }
12497         }
12498         return null;
12499     },
12500    
12501 /**
12502  * Inserts an item at the specified index in the collection.
12503  * @param {Number} index The index to insert the item at.
12504  * @param {String} key The key to associate with the new item, or the item itself.
12505  * @param {Object} o  (optional) If the second parameter was a key, the new item.
12506  * @return {Object} The item inserted.
12507  */
12508     insert : function(index, key, o){
12509         if(arguments.length == 2){
12510             o = arguments[1];
12511             key = this.getKey(o);
12512         }
12513         if(index >= this.length){
12514             return this.add(key, o);
12515         }
12516         this.length++;
12517         this.items.splice(index, 0, o);
12518         if(typeof key != "undefined" && key != null){
12519             this.map[key] = o;
12520         }
12521         this.keys.splice(index, 0, key);
12522         this.fireEvent("add", index, o, key);
12523         return o;
12524     },
12525    
12526 /**
12527  * Removed an item from the collection.
12528  * @param {Object} o The item to remove.
12529  * @return {Object} The item removed.
12530  */
12531     remove : function(o){
12532         return this.removeAt(this.indexOf(o));
12533     },
12534    
12535 /**
12536  * Remove an item from a specified index in the collection.
12537  * @param {Number} index The index within the collection of the item to remove.
12538  */
12539     removeAt : function(index){
12540         if(index < this.length && index >= 0){
12541             this.length--;
12542             var o = this.items[index];
12543             this.items.splice(index, 1);
12544             var key = this.keys[index];
12545             if(typeof key != "undefined"){
12546                 delete this.map[key];
12547             }
12548             this.keys.splice(index, 1);
12549             this.fireEvent("remove", o, key);
12550         }
12551     },
12552    
12553 /**
12554  * Removed an item associated with the passed key fom the collection.
12555  * @param {String} key The key of the item to remove.
12556  */
12557     removeKey : function(key){
12558         return this.removeAt(this.indexOfKey(key));
12559     },
12560    
12561 /**
12562  * Returns the number of items in the collection.
12563  * @return {Number} the number of items in the collection.
12564  */
12565     getCount : function(){
12566         return this.length; 
12567     },
12568    
12569 /**
12570  * Returns index within the collection of the passed Object.
12571  * @param {Object} o The item to find the index of.
12572  * @return {Number} index of the item.
12573  */
12574     indexOf : function(o){
12575         if(!this.items.indexOf){
12576             for(var i = 0, len = this.items.length; i < len; i++){
12577                 if(this.items[i] == o) return i;
12578             }
12579             return -1;
12580         }else{
12581             return this.items.indexOf(o);
12582         }
12583     },
12584    
12585 /**
12586  * Returns index within the collection of the passed key.
12587  * @param {String} key The key to find the index of.
12588  * @return {Number} index of the key.
12589  */
12590     indexOfKey : function(key){
12591         if(!this.keys.indexOf){
12592             for(var i = 0, len = this.keys.length; i < len; i++){
12593                 if(this.keys[i] == key) return i;
12594             }
12595             return -1;
12596         }else{
12597             return this.keys.indexOf(key);
12598         }
12599     },
12600    
12601 /**
12602  * Returns the item associated with the passed key OR index. Key has priority over index.
12603  * @param {String/Number} key The key or index of the item.
12604  * @return {Object} The item associated with the passed key.
12605  */
12606     item : function(key){
12607         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
12608         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
12609     },
12610     
12611 /**
12612  * Returns the item at the specified index.
12613  * @param {Number} index The index of the item.
12614  * @return {Object}
12615  */
12616     itemAt : function(index){
12617         return this.items[index];
12618     },
12619     
12620 /**
12621  * Returns the item associated with the passed key.
12622  * @param {String/Number} key The key of the item.
12623  * @return {Object} The item associated with the passed key.
12624  */
12625     key : function(key){
12626         return this.map[key];
12627     },
12628    
12629 /**
12630  * Returns true if the collection contains the passed Object as an item.
12631  * @param {Object} o  The Object to look for in the collection.
12632  * @return {Boolean} True if the collection contains the Object as an item.
12633  */
12634     contains : function(o){
12635         return this.indexOf(o) != -1;
12636     },
12637    
12638 /**
12639  * Returns true if the collection contains the passed Object as a key.
12640  * @param {String} key The key to look for in the collection.
12641  * @return {Boolean} True if the collection contains the Object as a key.
12642  */
12643     containsKey : function(key){
12644         return typeof this.map[key] != "undefined";
12645     },
12646    
12647 /**
12648  * Removes all items from the collection.
12649  */
12650     clear : function(){
12651         this.length = 0;
12652         this.items = [];
12653         this.keys = [];
12654         this.map = {};
12655         this.fireEvent("clear");
12656     },
12657    
12658 /**
12659  * Returns the first item in the collection.
12660  * @return {Object} the first item in the collection..
12661  */
12662     first : function(){
12663         return this.items[0]; 
12664     },
12665    
12666 /**
12667  * Returns the last item in the collection.
12668  * @return {Object} the last item in the collection..
12669  */
12670     last : function(){
12671         return this.items[this.length-1];   
12672     },
12673     
12674     _sort : function(property, dir, fn){
12675         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
12676         fn = fn || function(a, b){
12677             return a-b;
12678         };
12679         var c = [], k = this.keys, items = this.items;
12680         for(var i = 0, len = items.length; i < len; i++){
12681             c[c.length] = {key: k[i], value: items[i], index: i};
12682         }
12683         c.sort(function(a, b){
12684             var v = fn(a[property], b[property]) * dsc;
12685             if(v == 0){
12686                 v = (a.index < b.index ? -1 : 1);
12687             }
12688             return v;
12689         });
12690         for(var i = 0, len = c.length; i < len; i++){
12691             items[i] = c[i].value;
12692             k[i] = c[i].key;
12693         }
12694         this.fireEvent("sort", this);
12695     },
12696     
12697     /**
12698      * Sorts this collection with the passed comparison function
12699      * @param {String} direction (optional) "ASC" or "DESC"
12700      * @param {Function} fn (optional) comparison function
12701      */
12702     sort : function(dir, fn){
12703         this._sort("value", dir, fn);
12704     },
12705     
12706     /**
12707      * Sorts this collection by keys
12708      * @param {String} direction (optional) "ASC" or "DESC"
12709      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
12710      */
12711     keySort : function(dir, fn){
12712         this._sort("key", dir, fn || function(a, b){
12713             return String(a).toUpperCase()-String(b).toUpperCase();
12714         });
12715     },
12716     
12717     /**
12718      * Returns a range of items in this collection
12719      * @param {Number} startIndex (optional) defaults to 0
12720      * @param {Number} endIndex (optional) default to the last item
12721      * @return {Array} An array of items
12722      */
12723     getRange : function(start, end){
12724         var items = this.items;
12725         if(items.length < 1){
12726             return [];
12727         }
12728         start = start || 0;
12729         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
12730         var r = [];
12731         if(start <= end){
12732             for(var i = start; i <= end; i++) {
12733                     r[r.length] = items[i];
12734             }
12735         }else{
12736             for(var i = start; i >= end; i--) {
12737                     r[r.length] = items[i];
12738             }
12739         }
12740         return r;
12741     },
12742         
12743     /**
12744      * Filter the <i>objects</i> in this collection by a specific property. 
12745      * Returns a new collection that has been filtered.
12746      * @param {String} property A property on your objects
12747      * @param {String/RegExp} value Either string that the property values 
12748      * should start with or a RegExp to test against the property
12749      * @return {MixedCollection} The new filtered collection
12750      */
12751     filter : function(property, value){
12752         if(!value.exec){ // not a regex
12753             value = String(value);
12754             if(value.length == 0){
12755                 return this.clone();
12756             }
12757             value = new RegExp("^" + Roo.escapeRe(value), "i");
12758         }
12759         return this.filterBy(function(o){
12760             return o && value.test(o[property]);
12761         });
12762         },
12763     
12764     /**
12765      * Filter by a function. * Returns a new collection that has been filtered.
12766      * The passed function will be called with each 
12767      * object in the collection. If the function returns true, the value is included 
12768      * otherwise it is filtered.
12769      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
12770      * @param {Object} scope (optional) The scope of the function (defaults to this) 
12771      * @return {MixedCollection} The new filtered collection
12772      */
12773     filterBy : function(fn, scope){
12774         var r = new Roo.util.MixedCollection();
12775         r.getKey = this.getKey;
12776         var k = this.keys, it = this.items;
12777         for(var i = 0, len = it.length; i < len; i++){
12778             if(fn.call(scope||this, it[i], k[i])){
12779                                 r.add(k[i], it[i]);
12780                         }
12781         }
12782         return r;
12783     },
12784     
12785     /**
12786      * Creates a duplicate of this collection
12787      * @return {MixedCollection}
12788      */
12789     clone : function(){
12790         var r = new Roo.util.MixedCollection();
12791         var k = this.keys, it = this.items;
12792         for(var i = 0, len = it.length; i < len; i++){
12793             r.add(k[i], it[i]);
12794         }
12795         r.getKey = this.getKey;
12796         return r;
12797     }
12798 });
12799 /**
12800  * Returns the item associated with the passed key or index.
12801  * @method
12802  * @param {String/Number} key The key or index of the item.
12803  * @return {Object} The item associated with the passed key.
12804  */
12805 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
12806  * Based on:
12807  * Ext JS Library 1.1.1
12808  * Copyright(c) 2006-2007, Ext JS, LLC.
12809  *
12810  * Originally Released Under LGPL - original licence link has changed is not relivant.
12811  *
12812  * Fork - LGPL
12813  * <script type="text/javascript">
12814  */
12815 /**
12816  * @class Roo.util.JSON
12817  * Modified version of Douglas Crockford"s json.js that doesn"t
12818  * mess with the Object prototype 
12819  * http://www.json.org/js.html
12820  * @singleton
12821  */
12822 Roo.util.JSON = new (function(){
12823     var useHasOwn = {}.hasOwnProperty ? true : false;
12824     
12825     // crashes Safari in some instances
12826     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
12827     
12828     var pad = function(n) {
12829         return n < 10 ? "0" + n : n;
12830     };
12831     
12832     var m = {
12833         "\b": '\\b',
12834         "\t": '\\t',
12835         "\n": '\\n',
12836         "\f": '\\f',
12837         "\r": '\\r',
12838         '"' : '\\"',
12839         "\\": '\\\\'
12840     };
12841
12842     var encodeString = function(s){
12843         if (/["\\\x00-\x1f]/.test(s)) {
12844             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
12845                 var c = m[b];
12846                 if(c){
12847                     return c;
12848                 }
12849                 c = b.charCodeAt();
12850                 return "\\u00" +
12851                     Math.floor(c / 16).toString(16) +
12852                     (c % 16).toString(16);
12853             }) + '"';
12854         }
12855         return '"' + s + '"';
12856     };
12857     
12858     var encodeArray = function(o){
12859         var a = ["["], b, i, l = o.length, v;
12860             for (i = 0; i < l; i += 1) {
12861                 v = o[i];
12862                 switch (typeof v) {
12863                     case "undefined":
12864                     case "function":
12865                     case "unknown":
12866                         break;
12867                     default:
12868                         if (b) {
12869                             a.push(',');
12870                         }
12871                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
12872                         b = true;
12873                 }
12874             }
12875             a.push("]");
12876             return a.join("");
12877     };
12878     
12879     var encodeDate = function(o){
12880         return '"' + o.getFullYear() + "-" +
12881                 pad(o.getMonth() + 1) + "-" +
12882                 pad(o.getDate()) + "T" +
12883                 pad(o.getHours()) + ":" +
12884                 pad(o.getMinutes()) + ":" +
12885                 pad(o.getSeconds()) + '"';
12886     };
12887     
12888     /**
12889      * Encodes an Object, Array or other value
12890      * @param {Mixed} o The variable to encode
12891      * @return {String} The JSON string
12892      */
12893     this.encode = function(o)
12894     {
12895         // should this be extended to fully wrap stringify..
12896         
12897         if(typeof o == "undefined" || o === null){
12898             return "null";
12899         }else if(o instanceof Array){
12900             return encodeArray(o);
12901         }else if(o instanceof Date){
12902             return encodeDate(o);
12903         }else if(typeof o == "string"){
12904             return encodeString(o);
12905         }else if(typeof o == "number"){
12906             return isFinite(o) ? String(o) : "null";
12907         }else if(typeof o == "boolean"){
12908             return String(o);
12909         }else {
12910             var a = ["{"], b, i, v;
12911             for (i in o) {
12912                 if(!useHasOwn || o.hasOwnProperty(i)) {
12913                     v = o[i];
12914                     switch (typeof v) {
12915                     case "undefined":
12916                     case "function":
12917                     case "unknown":
12918                         break;
12919                     default:
12920                         if(b){
12921                             a.push(',');
12922                         }
12923                         a.push(this.encode(i), ":",
12924                                 v === null ? "null" : this.encode(v));
12925                         b = true;
12926                     }
12927                 }
12928             }
12929             a.push("}");
12930             return a.join("");
12931         }
12932     };
12933     
12934     /**
12935      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
12936      * @param {String} json The JSON string
12937      * @return {Object} The resulting object
12938      */
12939     this.decode = function(json){
12940         
12941         return  /** eval:var:json */ eval("(" + json + ')');
12942     };
12943 })();
12944 /** 
12945  * Shorthand for {@link Roo.util.JSON#encode}
12946  * @member Roo encode 
12947  * @method */
12948 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
12949 /** 
12950  * Shorthand for {@link Roo.util.JSON#decode}
12951  * @member Roo decode 
12952  * @method */
12953 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
12954 /*
12955  * Based on:
12956  * Ext JS Library 1.1.1
12957  * Copyright(c) 2006-2007, Ext JS, LLC.
12958  *
12959  * Originally Released Under LGPL - original licence link has changed is not relivant.
12960  *
12961  * Fork - LGPL
12962  * <script type="text/javascript">
12963  */
12964  
12965 /**
12966  * @class Roo.util.Format
12967  * Reusable data formatting functions
12968  * @singleton
12969  */
12970 Roo.util.Format = function(){
12971     var trimRe = /^\s+|\s+$/g;
12972     return {
12973         /**
12974          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
12975          * @param {String} value The string to truncate
12976          * @param {Number} length The maximum length to allow before truncating
12977          * @return {String} The converted text
12978          */
12979         ellipsis : function(value, len){
12980             if(value && value.length > len){
12981                 return value.substr(0, len-3)+"...";
12982             }
12983             return value;
12984         },
12985
12986         /**
12987          * Checks a reference and converts it to empty string if it is undefined
12988          * @param {Mixed} value Reference to check
12989          * @return {Mixed} Empty string if converted, otherwise the original value
12990          */
12991         undef : function(value){
12992             return typeof value != "undefined" ? value : "";
12993         },
12994
12995         /**
12996          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
12997          * @param {String} value The string to encode
12998          * @return {String} The encoded text
12999          */
13000         htmlEncode : function(value){
13001             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
13002         },
13003
13004         /**
13005          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
13006          * @param {String} value The string to decode
13007          * @return {String} The decoded text
13008          */
13009         htmlDecode : function(value){
13010             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
13011         },
13012
13013         /**
13014          * Trims any whitespace from either side of a string
13015          * @param {String} value The text to trim
13016          * @return {String} The trimmed text
13017          */
13018         trim : function(value){
13019             return String(value).replace(trimRe, "");
13020         },
13021
13022         /**
13023          * Returns a substring from within an original string
13024          * @param {String} value The original text
13025          * @param {Number} start The start index of the substring
13026          * @param {Number} length The length of the substring
13027          * @return {String} The substring
13028          */
13029         substr : function(value, start, length){
13030             return String(value).substr(start, length);
13031         },
13032
13033         /**
13034          * Converts a string to all lower case letters
13035          * @param {String} value The text to convert
13036          * @return {String} The converted text
13037          */
13038         lowercase : function(value){
13039             return String(value).toLowerCase();
13040         },
13041
13042         /**
13043          * Converts a string to all upper case letters
13044          * @param {String} value The text to convert
13045          * @return {String} The converted text
13046          */
13047         uppercase : function(value){
13048             return String(value).toUpperCase();
13049         },
13050
13051         /**
13052          * Converts the first character only of a string to upper case
13053          * @param {String} value The text to convert
13054          * @return {String} The converted text
13055          */
13056         capitalize : function(value){
13057             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13058         },
13059
13060         // private
13061         call : function(value, fn){
13062             if(arguments.length > 2){
13063                 var args = Array.prototype.slice.call(arguments, 2);
13064                 args.unshift(value);
13065                  
13066                 return /** eval:var:value */  eval(fn).apply(window, args);
13067             }else{
13068                 /** eval:var:value */
13069                 return /** eval:var:value */ eval(fn).call(window, value);
13070             }
13071         },
13072
13073        
13074         /**
13075          * safer version of Math.toFixed..??/
13076          * @param {Number/String} value The numeric value to format
13077          * @param {Number/String} value Decimal places 
13078          * @return {String} The formatted currency string
13079          */
13080         toFixed : function(v, n)
13081         {
13082             // why not use to fixed - precision is buggered???
13083             if (!n) {
13084                 return Math.round(v-0);
13085             }
13086             var fact = Math.pow(10,n+1);
13087             v = (Math.round((v-0)*fact))/fact;
13088             var z = (''+fact).substring(2);
13089             if (v == Math.floor(v)) {
13090                 return Math.floor(v) + '.' + z;
13091             }
13092             
13093             // now just padd decimals..
13094             var ps = String(v).split('.');
13095             var fd = (ps[1] + z);
13096             var r = fd.substring(0,n); 
13097             var rm = fd.substring(n); 
13098             if (rm < 5) {
13099                 return ps[0] + '.' + r;
13100             }
13101             r*=1; // turn it into a number;
13102             r++;
13103             if (String(r).length != n) {
13104                 ps[0]*=1;
13105                 ps[0]++;
13106                 r = String(r).substring(1); // chop the end off.
13107             }
13108             
13109             return ps[0] + '.' + r;
13110              
13111         },
13112         
13113         /**
13114          * Format a number as US currency
13115          * @param {Number/String} value The numeric value to format
13116          * @return {String} The formatted currency string
13117          */
13118         usMoney : function(v){
13119             v = (Math.round((v-0)*100))/100;
13120             v = (v == Math.floor(v)) ? v + ".00" : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13121             v = String(v);
13122             var ps = v.split('.');
13123             var whole = ps[0];
13124             var sub = ps[1] ? '.'+ ps[1] : '.00';
13125             var r = /(\d+)(\d{3})/;
13126             while (r.test(whole)) {
13127                 whole = whole.replace(r, '$1' + ',' + '$2');
13128             }
13129             return "$" + whole + sub ;
13130         },
13131         
13132         /**
13133          * Parse a value into a formatted date using the specified format pattern.
13134          * @param {Mixed} value The value to format
13135          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13136          * @return {String} The formatted date string
13137          */
13138         date : function(v, format){
13139             if(!v){
13140                 return "";
13141             }
13142             if(!(v instanceof Date)){
13143                 v = new Date(Date.parse(v));
13144             }
13145             return v.dateFormat(format || "m/d/Y");
13146         },
13147
13148         /**
13149          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13150          * @param {String} format Any valid date format string
13151          * @return {Function} The date formatting function
13152          */
13153         dateRenderer : function(format){
13154             return function(v){
13155                 return Roo.util.Format.date(v, format);  
13156             };
13157         },
13158
13159         // private
13160         stripTagsRE : /<\/?[^>]+>/gi,
13161         
13162         /**
13163          * Strips all HTML tags
13164          * @param {Mixed} value The text from which to strip tags
13165          * @return {String} The stripped text
13166          */
13167         stripTags : function(v){
13168             return !v ? v : String(v).replace(this.stripTagsRE, "");
13169         }
13170     };
13171 }();/*
13172  * Based on:
13173  * Ext JS Library 1.1.1
13174  * Copyright(c) 2006-2007, Ext JS, LLC.
13175  *
13176  * Originally Released Under LGPL - original licence link has changed is not relivant.
13177  *
13178  * Fork - LGPL
13179  * <script type="text/javascript">
13180  */
13181
13182
13183  
13184
13185 /**
13186  * @class Roo.MasterTemplate
13187  * @extends Roo.Template
13188  * Provides a template that can have child templates. The syntax is:
13189 <pre><code>
13190 var t = new Roo.MasterTemplate(
13191         '&lt;select name="{name}"&gt;',
13192                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13193         '&lt;/select&gt;'
13194 );
13195 t.add('options', {value: 'foo', text: 'bar'});
13196 // or you can add multiple child elements in one shot
13197 t.addAll('options', [
13198     {value: 'foo', text: 'bar'},
13199     {value: 'foo2', text: 'bar2'},
13200     {value: 'foo3', text: 'bar3'}
13201 ]);
13202 // then append, applying the master template values
13203 t.append('my-form', {name: 'my-select'});
13204 </code></pre>
13205 * A name attribute for the child template is not required if you have only one child
13206 * template or you want to refer to them by index.
13207  */
13208 Roo.MasterTemplate = function(){
13209     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13210     this.originalHtml = this.html;
13211     var st = {};
13212     var m, re = this.subTemplateRe;
13213     re.lastIndex = 0;
13214     var subIndex = 0;
13215     while(m = re.exec(this.html)){
13216         var name = m[1], content = m[2];
13217         st[subIndex] = {
13218             name: name,
13219             index: subIndex,
13220             buffer: [],
13221             tpl : new Roo.Template(content)
13222         };
13223         if(name){
13224             st[name] = st[subIndex];
13225         }
13226         st[subIndex].tpl.compile();
13227         st[subIndex].tpl.call = this.call.createDelegate(this);
13228         subIndex++;
13229     }
13230     this.subCount = subIndex;
13231     this.subs = st;
13232 };
13233 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13234     /**
13235     * The regular expression used to match sub templates
13236     * @type RegExp
13237     * @property
13238     */
13239     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13240
13241     /**
13242      * Applies the passed values to a child template.
13243      * @param {String/Number} name (optional) The name or index of the child template
13244      * @param {Array/Object} values The values to be applied to the template
13245      * @return {MasterTemplate} this
13246      */
13247      add : function(name, values){
13248         if(arguments.length == 1){
13249             values = arguments[0];
13250             name = 0;
13251         }
13252         var s = this.subs[name];
13253         s.buffer[s.buffer.length] = s.tpl.apply(values);
13254         return this;
13255     },
13256
13257     /**
13258      * Applies all the passed values to a child template.
13259      * @param {String/Number} name (optional) The name or index of the child template
13260      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13261      * @param {Boolean} reset (optional) True to reset the template first
13262      * @return {MasterTemplate} this
13263      */
13264     fill : function(name, values, reset){
13265         var a = arguments;
13266         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13267             values = a[0];
13268             name = 0;
13269             reset = a[1];
13270         }
13271         if(reset){
13272             this.reset();
13273         }
13274         for(var i = 0, len = values.length; i < len; i++){
13275             this.add(name, values[i]);
13276         }
13277         return this;
13278     },
13279
13280     /**
13281      * Resets the template for reuse
13282      * @return {MasterTemplate} this
13283      */
13284      reset : function(){
13285         var s = this.subs;
13286         for(var i = 0; i < this.subCount; i++){
13287             s[i].buffer = [];
13288         }
13289         return this;
13290     },
13291
13292     applyTemplate : function(values){
13293         var s = this.subs;
13294         var replaceIndex = -1;
13295         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13296             return s[++replaceIndex].buffer.join("");
13297         });
13298         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13299     },
13300
13301     apply : function(){
13302         return this.applyTemplate.apply(this, arguments);
13303     },
13304
13305     compile : function(){return this;}
13306 });
13307
13308 /**
13309  * Alias for fill().
13310  * @method
13311  */
13312 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13313  /**
13314  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13315  * var tpl = Roo.MasterTemplate.from('element-id');
13316  * @param {String/HTMLElement} el
13317  * @param {Object} config
13318  * @static
13319  */
13320 Roo.MasterTemplate.from = function(el, config){
13321     el = Roo.getDom(el);
13322     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13323 };/*
13324  * Based on:
13325  * Ext JS Library 1.1.1
13326  * Copyright(c) 2006-2007, Ext JS, LLC.
13327  *
13328  * Originally Released Under LGPL - original licence link has changed is not relivant.
13329  *
13330  * Fork - LGPL
13331  * <script type="text/javascript">
13332  */
13333
13334  
13335 /**
13336  * @class Roo.util.CSS
13337  * Utility class for manipulating CSS rules
13338  * @singleton
13339  */
13340 Roo.util.CSS = function(){
13341         var rules = null;
13342         var doc = document;
13343
13344     var camelRe = /(-[a-z])/gi;
13345     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13346
13347    return {
13348    /**
13349     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
13350     * tag and appended to the HEAD of the document.
13351     * @param {String|Object} cssText The text containing the css rules
13352     * @param {String} id An id to add to the stylesheet for later removal
13353     * @return {StyleSheet}
13354     */
13355     createStyleSheet : function(cssText, id){
13356         var ss;
13357         var head = doc.getElementsByTagName("head")[0];
13358         var nrules = doc.createElement("style");
13359         nrules.setAttribute("type", "text/css");
13360         if(id){
13361             nrules.setAttribute("id", id);
13362         }
13363         if (typeof(cssText) != 'string') {
13364             // support object maps..
13365             // not sure if this a good idea.. 
13366             // perhaps it should be merged with the general css handling
13367             // and handle js style props.
13368             var cssTextNew = [];
13369             for(var n in cssText) {
13370                 var citems = [];
13371                 for(var k in cssText[n]) {
13372                     citems.push( k + ' : ' +cssText[n][k] + ';' );
13373                 }
13374                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
13375                 
13376             }
13377             cssText = cssTextNew.join("\n");
13378             
13379         }
13380        
13381        
13382        if(Roo.isIE){
13383            head.appendChild(nrules);
13384            ss = nrules.styleSheet;
13385            ss.cssText = cssText;
13386        }else{
13387            try{
13388                 nrules.appendChild(doc.createTextNode(cssText));
13389            }catch(e){
13390                nrules.cssText = cssText; 
13391            }
13392            head.appendChild(nrules);
13393            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
13394        }
13395        this.cacheStyleSheet(ss);
13396        return ss;
13397    },
13398
13399    /**
13400     * Removes a style or link tag by id
13401     * @param {String} id The id of the tag
13402     */
13403    removeStyleSheet : function(id){
13404        var existing = doc.getElementById(id);
13405        if(existing){
13406            existing.parentNode.removeChild(existing);
13407        }
13408    },
13409
13410    /**
13411     * Dynamically swaps an existing stylesheet reference for a new one
13412     * @param {String} id The id of an existing link tag to remove
13413     * @param {String} url The href of the new stylesheet to include
13414     */
13415    swapStyleSheet : function(id, url){
13416        this.removeStyleSheet(id);
13417        var ss = doc.createElement("link");
13418        ss.setAttribute("rel", "stylesheet");
13419        ss.setAttribute("type", "text/css");
13420        ss.setAttribute("id", id);
13421        ss.setAttribute("href", url);
13422        doc.getElementsByTagName("head")[0].appendChild(ss);
13423    },
13424    
13425    /**
13426     * Refresh the rule cache if you have dynamically added stylesheets
13427     * @return {Object} An object (hash) of rules indexed by selector
13428     */
13429    refreshCache : function(){
13430        return this.getRules(true);
13431    },
13432
13433    // private
13434    cacheStyleSheet : function(stylesheet){
13435        if(!rules){
13436            rules = {};
13437        }
13438        try{// try catch for cross domain access issue
13439            var ssRules = stylesheet.cssRules || stylesheet.rules;
13440            for(var j = ssRules.length-1; j >= 0; --j){
13441                rules[ssRules[j].selectorText] = ssRules[j];
13442            }
13443        }catch(e){}
13444    },
13445    
13446    /**
13447     * Gets all css rules for the document
13448     * @param {Boolean} refreshCache true to refresh the internal cache
13449     * @return {Object} An object (hash) of rules indexed by selector
13450     */
13451    getRules : function(refreshCache){
13452                 if(rules == null || refreshCache){
13453                         rules = {};
13454                         var ds = doc.styleSheets;
13455                         for(var i =0, len = ds.length; i < len; i++){
13456                             try{
13457                         this.cacheStyleSheet(ds[i]);
13458                     }catch(e){} 
13459                 }
13460                 }
13461                 return rules;
13462         },
13463         
13464         /**
13465     * Gets an an individual CSS rule by selector(s)
13466     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
13467     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
13468     * @return {CSSRule} The CSS rule or null if one is not found
13469     */
13470    getRule : function(selector, refreshCache){
13471                 var rs = this.getRules(refreshCache);
13472                 if(!(selector instanceof Array)){
13473                     return rs[selector];
13474                 }
13475                 for(var i = 0; i < selector.length; i++){
13476                         if(rs[selector[i]]){
13477                                 return rs[selector[i]];
13478                         }
13479                 }
13480                 return null;
13481         },
13482         
13483         
13484         /**
13485     * Updates a rule property
13486     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
13487     * @param {String} property The css property
13488     * @param {String} value The new value for the property
13489     * @return {Boolean} true If a rule was found and updated
13490     */
13491    updateRule : function(selector, property, value){
13492                 if(!(selector instanceof Array)){
13493                         var rule = this.getRule(selector);
13494                         if(rule){
13495                                 rule.style[property.replace(camelRe, camelFn)] = value;
13496                                 return true;
13497                         }
13498                 }else{
13499                         for(var i = 0; i < selector.length; i++){
13500                                 if(this.updateRule(selector[i], property, value)){
13501                                         return true;
13502                                 }
13503                         }
13504                 }
13505                 return false;
13506         }
13507    };   
13508 }();/*
13509  * Based on:
13510  * Ext JS Library 1.1.1
13511  * Copyright(c) 2006-2007, Ext JS, LLC.
13512  *
13513  * Originally Released Under LGPL - original licence link has changed is not relivant.
13514  *
13515  * Fork - LGPL
13516  * <script type="text/javascript">
13517  */
13518
13519  
13520
13521 /**
13522  * @class Roo.util.ClickRepeater
13523  * @extends Roo.util.Observable
13524  * 
13525  * A wrapper class which can be applied to any element. Fires a "click" event while the
13526  * mouse is pressed. The interval between firings may be specified in the config but
13527  * defaults to 10 milliseconds.
13528  * 
13529  * Optionally, a CSS class may be applied to the element during the time it is pressed.
13530  * 
13531  * @cfg {String/HTMLElement/Element} el The element to act as a button.
13532  * @cfg {Number} delay The initial delay before the repeating event begins firing.
13533  * Similar to an autorepeat key delay.
13534  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
13535  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
13536  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
13537  *           "interval" and "delay" are ignored. "immediate" is honored.
13538  * @cfg {Boolean} preventDefault True to prevent the default click event
13539  * @cfg {Boolean} stopDefault True to stop the default click event
13540  * 
13541  * @history
13542  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
13543  *     2007-02-02 jvs Renamed to ClickRepeater
13544  *   2007-02-03 jvs Modifications for FF Mac and Safari 
13545  *
13546  *  @constructor
13547  * @param {String/HTMLElement/Element} el The element to listen on
13548  * @param {Object} config
13549  **/
13550 Roo.util.ClickRepeater = function(el, config)
13551 {
13552     this.el = Roo.get(el);
13553     this.el.unselectable();
13554
13555     Roo.apply(this, config);
13556
13557     this.addEvents({
13558     /**
13559      * @event mousedown
13560      * Fires when the mouse button is depressed.
13561      * @param {Roo.util.ClickRepeater} this
13562      */
13563         "mousedown" : true,
13564     /**
13565      * @event click
13566      * Fires on a specified interval during the time the element is pressed.
13567      * @param {Roo.util.ClickRepeater} this
13568      */
13569         "click" : true,
13570     /**
13571      * @event mouseup
13572      * Fires when the mouse key is released.
13573      * @param {Roo.util.ClickRepeater} this
13574      */
13575         "mouseup" : true
13576     });
13577
13578     this.el.on("mousedown", this.handleMouseDown, this);
13579     if(this.preventDefault || this.stopDefault){
13580         this.el.on("click", function(e){
13581             if(this.preventDefault){
13582                 e.preventDefault();
13583             }
13584             if(this.stopDefault){
13585                 e.stopEvent();
13586             }
13587         }, this);
13588     }
13589
13590     // allow inline handler
13591     if(this.handler){
13592         this.on("click", this.handler,  this.scope || this);
13593     }
13594
13595     Roo.util.ClickRepeater.superclass.constructor.call(this);
13596 };
13597
13598 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
13599     interval : 20,
13600     delay: 250,
13601     preventDefault : true,
13602     stopDefault : false,
13603     timer : 0,
13604
13605     // private
13606     handleMouseDown : function(){
13607         clearTimeout(this.timer);
13608         this.el.blur();
13609         if(this.pressClass){
13610             this.el.addClass(this.pressClass);
13611         }
13612         this.mousedownTime = new Date();
13613
13614         Roo.get(document).on("mouseup", this.handleMouseUp, this);
13615         this.el.on("mouseout", this.handleMouseOut, this);
13616
13617         this.fireEvent("mousedown", this);
13618         this.fireEvent("click", this);
13619         
13620         this.timer = this.click.defer(this.delay || this.interval, this);
13621     },
13622
13623     // private
13624     click : function(){
13625         this.fireEvent("click", this);
13626         this.timer = this.click.defer(this.getInterval(), this);
13627     },
13628
13629     // private
13630     getInterval: function(){
13631         if(!this.accelerate){
13632             return this.interval;
13633         }
13634         var pressTime = this.mousedownTime.getElapsed();
13635         if(pressTime < 500){
13636             return 400;
13637         }else if(pressTime < 1700){
13638             return 320;
13639         }else if(pressTime < 2600){
13640             return 250;
13641         }else if(pressTime < 3500){
13642             return 180;
13643         }else if(pressTime < 4400){
13644             return 140;
13645         }else if(pressTime < 5300){
13646             return 80;
13647         }else if(pressTime < 6200){
13648             return 50;
13649         }else{
13650             return 10;
13651         }
13652     },
13653
13654     // private
13655     handleMouseOut : function(){
13656         clearTimeout(this.timer);
13657         if(this.pressClass){
13658             this.el.removeClass(this.pressClass);
13659         }
13660         this.el.on("mouseover", this.handleMouseReturn, this);
13661     },
13662
13663     // private
13664     handleMouseReturn : function(){
13665         this.el.un("mouseover", this.handleMouseReturn);
13666         if(this.pressClass){
13667             this.el.addClass(this.pressClass);
13668         }
13669         this.click();
13670     },
13671
13672     // private
13673     handleMouseUp : function(){
13674         clearTimeout(this.timer);
13675         this.el.un("mouseover", this.handleMouseReturn);
13676         this.el.un("mouseout", this.handleMouseOut);
13677         Roo.get(document).un("mouseup", this.handleMouseUp);
13678         this.el.removeClass(this.pressClass);
13679         this.fireEvent("mouseup", this);
13680     }
13681 });/*
13682  * Based on:
13683  * Ext JS Library 1.1.1
13684  * Copyright(c) 2006-2007, Ext JS, LLC.
13685  *
13686  * Originally Released Under LGPL - original licence link has changed is not relivant.
13687  *
13688  * Fork - LGPL
13689  * <script type="text/javascript">
13690  */
13691
13692  
13693 /**
13694  * @class Roo.KeyNav
13695  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
13696  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
13697  * way to implement custom navigation schemes for any UI component.</p>
13698  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
13699  * pageUp, pageDown, del, home, end.  Usage:</p>
13700  <pre><code>
13701 var nav = new Roo.KeyNav("my-element", {
13702     "left" : function(e){
13703         this.moveLeft(e.ctrlKey);
13704     },
13705     "right" : function(e){
13706         this.moveRight(e.ctrlKey);
13707     },
13708     "enter" : function(e){
13709         this.save();
13710     },
13711     scope : this
13712 });
13713 </code></pre>
13714  * @constructor
13715  * @param {String/HTMLElement/Roo.Element} el The element to bind to
13716  * @param {Object} config The config
13717  */
13718 Roo.KeyNav = function(el, config){
13719     this.el = Roo.get(el);
13720     Roo.apply(this, config);
13721     if(!this.disabled){
13722         this.disabled = true;
13723         this.enable();
13724     }
13725 };
13726
13727 Roo.KeyNav.prototype = {
13728     /**
13729      * @cfg {Boolean} disabled
13730      * True to disable this KeyNav instance (defaults to false)
13731      */
13732     disabled : false,
13733     /**
13734      * @cfg {String} defaultEventAction
13735      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
13736      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
13737      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
13738      */
13739     defaultEventAction: "stopEvent",
13740     /**
13741      * @cfg {Boolean} forceKeyDown
13742      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
13743      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
13744      * handle keydown instead of keypress.
13745      */
13746     forceKeyDown : false,
13747
13748     // private
13749     prepareEvent : function(e){
13750         var k = e.getKey();
13751         var h = this.keyToHandler[k];
13752         //if(h && this[h]){
13753         //    e.stopPropagation();
13754         //}
13755         if(Roo.isSafari && h && k >= 37 && k <= 40){
13756             e.stopEvent();
13757         }
13758     },
13759
13760     // private
13761     relay : function(e){
13762         var k = e.getKey();
13763         var h = this.keyToHandler[k];
13764         if(h && this[h]){
13765             if(this.doRelay(e, this[h], h) !== true){
13766                 e[this.defaultEventAction]();
13767             }
13768         }
13769     },
13770
13771     // private
13772     doRelay : function(e, h, hname){
13773         return h.call(this.scope || this, e);
13774     },
13775
13776     // possible handlers
13777     enter : false,
13778     left : false,
13779     right : false,
13780     up : false,
13781     down : false,
13782     tab : false,
13783     esc : false,
13784     pageUp : false,
13785     pageDown : false,
13786     del : false,
13787     home : false,
13788     end : false,
13789
13790     // quick lookup hash
13791     keyToHandler : {
13792         37 : "left",
13793         39 : "right",
13794         38 : "up",
13795         40 : "down",
13796         33 : "pageUp",
13797         34 : "pageDown",
13798         46 : "del",
13799         36 : "home",
13800         35 : "end",
13801         13 : "enter",
13802         27 : "esc",
13803         9  : "tab"
13804     },
13805
13806         /**
13807          * Enable this KeyNav
13808          */
13809         enable: function(){
13810                 if(this.disabled){
13811             // ie won't do special keys on keypress, no one else will repeat keys with keydown
13812             // the EventObject will normalize Safari automatically
13813             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
13814                 this.el.on("keydown", this.relay,  this);
13815             }else{
13816                 this.el.on("keydown", this.prepareEvent,  this);
13817                 this.el.on("keypress", this.relay,  this);
13818             }
13819                     this.disabled = false;
13820                 }
13821         },
13822
13823         /**
13824          * Disable this KeyNav
13825          */
13826         disable: function(){
13827                 if(!this.disabled){
13828                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
13829                 this.el.un("keydown", this.relay);
13830             }else{
13831                 this.el.un("keydown", this.prepareEvent);
13832                 this.el.un("keypress", this.relay);
13833             }
13834                     this.disabled = true;
13835                 }
13836         }
13837 };/*
13838  * Based on:
13839  * Ext JS Library 1.1.1
13840  * Copyright(c) 2006-2007, Ext JS, LLC.
13841  *
13842  * Originally Released Under LGPL - original licence link has changed is not relivant.
13843  *
13844  * Fork - LGPL
13845  * <script type="text/javascript">
13846  */
13847
13848  
13849 /**
13850  * @class Roo.KeyMap
13851  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
13852  * The constructor accepts the same config object as defined by {@link #addBinding}.
13853  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
13854  * combination it will call the function with this signature (if the match is a multi-key
13855  * combination the callback will still be called only once): (String key, Roo.EventObject e)
13856  * A KeyMap can also handle a string representation of keys.<br />
13857  * Usage:
13858  <pre><code>
13859 // map one key by key code
13860 var map = new Roo.KeyMap("my-element", {
13861     key: 13, // or Roo.EventObject.ENTER
13862     fn: myHandler,
13863     scope: myObject
13864 });
13865
13866 // map multiple keys to one action by string
13867 var map = new Roo.KeyMap("my-element", {
13868     key: "a\r\n\t",
13869     fn: myHandler,
13870     scope: myObject
13871 });
13872
13873 // map multiple keys to multiple actions by strings and array of codes
13874 var map = new Roo.KeyMap("my-element", [
13875     {
13876         key: [10,13],
13877         fn: function(){ alert("Return was pressed"); }
13878     }, {
13879         key: "abc",
13880         fn: function(){ alert('a, b or c was pressed'); }
13881     }, {
13882         key: "\t",
13883         ctrl:true,
13884         shift:true,
13885         fn: function(){ alert('Control + shift + tab was pressed.'); }
13886     }
13887 ]);
13888 </code></pre>
13889  * <b>Note: A KeyMap starts enabled</b>
13890  * @constructor
13891  * @param {String/HTMLElement/Roo.Element} el The element to bind to
13892  * @param {Object} config The config (see {@link #addBinding})
13893  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
13894  */
13895 Roo.KeyMap = function(el, config, eventName){
13896     this.el  = Roo.get(el);
13897     this.eventName = eventName || "keydown";
13898     this.bindings = [];
13899     if(config){
13900         this.addBinding(config);
13901     }
13902     this.enable();
13903 };
13904
13905 Roo.KeyMap.prototype = {
13906     /**
13907      * True to stop the event from bubbling and prevent the default browser action if the
13908      * key was handled by the KeyMap (defaults to false)
13909      * @type Boolean
13910      */
13911     stopEvent : false,
13912
13913     /**
13914      * Add a new binding to this KeyMap. The following config object properties are supported:
13915      * <pre>
13916 Property    Type             Description
13917 ----------  ---------------  ----------------------------------------------------------------------
13918 key         String/Array     A single keycode or an array of keycodes to handle
13919 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
13920 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
13921 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
13922 fn          Function         The function to call when KeyMap finds the expected key combination
13923 scope       Object           The scope of the callback function
13924 </pre>
13925      *
13926      * Usage:
13927      * <pre><code>
13928 // Create a KeyMap
13929 var map = new Roo.KeyMap(document, {
13930     key: Roo.EventObject.ENTER,
13931     fn: handleKey,
13932     scope: this
13933 });
13934
13935 //Add a new binding to the existing KeyMap later
13936 map.addBinding({
13937     key: 'abc',
13938     shift: true,
13939     fn: handleKey,
13940     scope: this
13941 });
13942 </code></pre>
13943      * @param {Object/Array} config A single KeyMap config or an array of configs
13944      */
13945         addBinding : function(config){
13946         if(config instanceof Array){
13947             for(var i = 0, len = config.length; i < len; i++){
13948                 this.addBinding(config[i]);
13949             }
13950             return;
13951         }
13952         var keyCode = config.key,
13953             shift = config.shift, 
13954             ctrl = config.ctrl, 
13955             alt = config.alt,
13956             fn = config.fn,
13957             scope = config.scope;
13958         if(typeof keyCode == "string"){
13959             var ks = [];
13960             var keyString = keyCode.toUpperCase();
13961             for(var j = 0, len = keyString.length; j < len; j++){
13962                 ks.push(keyString.charCodeAt(j));
13963             }
13964             keyCode = ks;
13965         }
13966         var keyArray = keyCode instanceof Array;
13967         var handler = function(e){
13968             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
13969                 var k = e.getKey();
13970                 if(keyArray){
13971                     for(var i = 0, len = keyCode.length; i < len; i++){
13972                         if(keyCode[i] == k){
13973                           if(this.stopEvent){
13974                               e.stopEvent();
13975                           }
13976                           fn.call(scope || window, k, e);
13977                           return;
13978                         }
13979                     }
13980                 }else{
13981                     if(k == keyCode){
13982                         if(this.stopEvent){
13983                            e.stopEvent();
13984                         }
13985                         fn.call(scope || window, k, e);
13986                     }
13987                 }
13988             }
13989         };
13990         this.bindings.push(handler);  
13991         },
13992
13993     /**
13994      * Shorthand for adding a single key listener
13995      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
13996      * following options:
13997      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
13998      * @param {Function} fn The function to call
13999      * @param {Object} scope (optional) The scope of the function
14000      */
14001     on : function(key, fn, scope){
14002         var keyCode, shift, ctrl, alt;
14003         if(typeof key == "object" && !(key instanceof Array)){
14004             keyCode = key.key;
14005             shift = key.shift;
14006             ctrl = key.ctrl;
14007             alt = key.alt;
14008         }else{
14009             keyCode = key;
14010         }
14011         this.addBinding({
14012             key: keyCode,
14013             shift: shift,
14014             ctrl: ctrl,
14015             alt: alt,
14016             fn: fn,
14017             scope: scope
14018         })
14019     },
14020
14021     // private
14022     handleKeyDown : function(e){
14023             if(this.enabled){ //just in case
14024             var b = this.bindings;
14025             for(var i = 0, len = b.length; i < len; i++){
14026                 b[i].call(this, e);
14027             }
14028             }
14029         },
14030         
14031         /**
14032          * Returns true if this KeyMap is enabled
14033          * @return {Boolean} 
14034          */
14035         isEnabled : function(){
14036             return this.enabled;  
14037         },
14038         
14039         /**
14040          * Enables this KeyMap
14041          */
14042         enable: function(){
14043                 if(!this.enabled){
14044                     this.el.on(this.eventName, this.handleKeyDown, this);
14045                     this.enabled = true;
14046                 }
14047         },
14048
14049         /**
14050          * Disable this KeyMap
14051          */
14052         disable: function(){
14053                 if(this.enabled){
14054                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14055                     this.enabled = false;
14056                 }
14057         }
14058 };/*
14059  * Based on:
14060  * Ext JS Library 1.1.1
14061  * Copyright(c) 2006-2007, Ext JS, LLC.
14062  *
14063  * Originally Released Under LGPL - original licence link has changed is not relivant.
14064  *
14065  * Fork - LGPL
14066  * <script type="text/javascript">
14067  */
14068
14069  
14070 /**
14071  * @class Roo.util.TextMetrics
14072  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14073  * wide, in pixels, a given block of text will be.
14074  * @singleton
14075  */
14076 Roo.util.TextMetrics = function(){
14077     var shared;
14078     return {
14079         /**
14080          * Measures the size of the specified text
14081          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14082          * that can affect the size of the rendered text
14083          * @param {String} text The text to measure
14084          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14085          * in order to accurately measure the text height
14086          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14087          */
14088         measure : function(el, text, fixedWidth){
14089             if(!shared){
14090                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14091             }
14092             shared.bind(el);
14093             shared.setFixedWidth(fixedWidth || 'auto');
14094             return shared.getSize(text);
14095         },
14096
14097         /**
14098          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14099          * the overhead of multiple calls to initialize the style properties on each measurement.
14100          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14101          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14102          * in order to accurately measure the text height
14103          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14104          */
14105         createInstance : function(el, fixedWidth){
14106             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14107         }
14108     };
14109 }();
14110
14111  
14112
14113 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14114     var ml = new Roo.Element(document.createElement('div'));
14115     document.body.appendChild(ml.dom);
14116     ml.position('absolute');
14117     ml.setLeftTop(-1000, -1000);
14118     ml.hide();
14119
14120     if(fixedWidth){
14121         ml.setWidth(fixedWidth);
14122     }
14123      
14124     var instance = {
14125         /**
14126          * Returns the size of the specified text based on the internal element's style and width properties
14127          * @memberOf Roo.util.TextMetrics.Instance#
14128          * @param {String} text The text to measure
14129          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14130          */
14131         getSize : function(text){
14132             ml.update(text);
14133             var s = ml.getSize();
14134             ml.update('');
14135             return s;
14136         },
14137
14138         /**
14139          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14140          * that can affect the size of the rendered text
14141          * @memberOf Roo.util.TextMetrics.Instance#
14142          * @param {String/HTMLElement} el The element, dom node or id
14143          */
14144         bind : function(el){
14145             ml.setStyle(
14146                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14147             );
14148         },
14149
14150         /**
14151          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14152          * to set a fixed width in order to accurately measure the text height.
14153          * @memberOf Roo.util.TextMetrics.Instance#
14154          * @param {Number} width The width to set on the element
14155          */
14156         setFixedWidth : function(width){
14157             ml.setWidth(width);
14158         },
14159
14160         /**
14161          * Returns the measured width of the specified text
14162          * @memberOf Roo.util.TextMetrics.Instance#
14163          * @param {String} text The text to measure
14164          * @return {Number} width The width in pixels
14165          */
14166         getWidth : function(text){
14167             ml.dom.style.width = 'auto';
14168             return this.getSize(text).width;
14169         },
14170
14171         /**
14172          * Returns the measured height of the specified text.  For multiline text, be sure to call
14173          * {@link #setFixedWidth} if necessary.
14174          * @memberOf Roo.util.TextMetrics.Instance#
14175          * @param {String} text The text to measure
14176          * @return {Number} height The height in pixels
14177          */
14178         getHeight : function(text){
14179             return this.getSize(text).height;
14180         }
14181     };
14182
14183     instance.bind(bindTo);
14184
14185     return instance;
14186 };
14187
14188 // backwards compat
14189 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14190  * Based on:
14191  * Ext JS Library 1.1.1
14192  * Copyright(c) 2006-2007, Ext JS, LLC.
14193  *
14194  * Originally Released Under LGPL - original licence link has changed is not relivant.
14195  *
14196  * Fork - LGPL
14197  * <script type="text/javascript">
14198  */
14199
14200 /**
14201  * @class Roo.state.Provider
14202  * Abstract base class for state provider implementations. This class provides methods
14203  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14204  * Provider interface.
14205  */
14206 Roo.state.Provider = function(){
14207     /**
14208      * @event statechange
14209      * Fires when a state change occurs.
14210      * @param {Provider} this This state provider
14211      * @param {String} key The state key which was changed
14212      * @param {String} value The encoded value for the state
14213      */
14214     this.addEvents({
14215         "statechange": true
14216     });
14217     this.state = {};
14218     Roo.state.Provider.superclass.constructor.call(this);
14219 };
14220 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14221     /**
14222      * Returns the current value for a key
14223      * @param {String} name The key name
14224      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14225      * @return {Mixed} The state data
14226      */
14227     get : function(name, defaultValue){
14228         return typeof this.state[name] == "undefined" ?
14229             defaultValue : this.state[name];
14230     },
14231     
14232     /**
14233      * Clears a value from the state
14234      * @param {String} name The key name
14235      */
14236     clear : function(name){
14237         delete this.state[name];
14238         this.fireEvent("statechange", this, name, null);
14239     },
14240     
14241     /**
14242      * Sets the value for a key
14243      * @param {String} name The key name
14244      * @param {Mixed} value The value to set
14245      */
14246     set : function(name, value){
14247         this.state[name] = value;
14248         this.fireEvent("statechange", this, name, value);
14249     },
14250     
14251     /**
14252      * Decodes a string previously encoded with {@link #encodeValue}.
14253      * @param {String} value The value to decode
14254      * @return {Mixed} The decoded value
14255      */
14256     decodeValue : function(cookie){
14257         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14258         var matches = re.exec(unescape(cookie));
14259         if(!matches || !matches[1]) return; // non state cookie
14260         var type = matches[1];
14261         var v = matches[2];
14262         switch(type){
14263             case "n":
14264                 return parseFloat(v);
14265             case "d":
14266                 return new Date(Date.parse(v));
14267             case "b":
14268                 return (v == "1");
14269             case "a":
14270                 var all = [];
14271                 var values = v.split("^");
14272                 for(var i = 0, len = values.length; i < len; i++){
14273                     all.push(this.decodeValue(values[i]));
14274                 }
14275                 return all;
14276            case "o":
14277                 var all = {};
14278                 var values = v.split("^");
14279                 for(var i = 0, len = values.length; i < len; i++){
14280                     var kv = values[i].split("=");
14281                     all[kv[0]] = this.decodeValue(kv[1]);
14282                 }
14283                 return all;
14284            default:
14285                 return v;
14286         }
14287     },
14288     
14289     /**
14290      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14291      * @param {Mixed} value The value to encode
14292      * @return {String} The encoded value
14293      */
14294     encodeValue : function(v){
14295         var enc;
14296         if(typeof v == "number"){
14297             enc = "n:" + v;
14298         }else if(typeof v == "boolean"){
14299             enc = "b:" + (v ? "1" : "0");
14300         }else if(v instanceof Date){
14301             enc = "d:" + v.toGMTString();
14302         }else if(v instanceof Array){
14303             var flat = "";
14304             for(var i = 0, len = v.length; i < len; i++){
14305                 flat += this.encodeValue(v[i]);
14306                 if(i != len-1) flat += "^";
14307             }
14308             enc = "a:" + flat;
14309         }else if(typeof v == "object"){
14310             var flat = "";
14311             for(var key in v){
14312                 if(typeof v[key] != "function"){
14313                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14314                 }
14315             }
14316             enc = "o:" + flat.substring(0, flat.length-1);
14317         }else{
14318             enc = "s:" + v;
14319         }
14320         return escape(enc);        
14321     }
14322 });
14323
14324 /*
14325  * Based on:
14326  * Ext JS Library 1.1.1
14327  * Copyright(c) 2006-2007, Ext JS, LLC.
14328  *
14329  * Originally Released Under LGPL - original licence link has changed is not relivant.
14330  *
14331  * Fork - LGPL
14332  * <script type="text/javascript">
14333  */
14334 /**
14335  * @class Roo.state.Manager
14336  * This is the global state manager. By default all components that are "state aware" check this class
14337  * for state information if you don't pass them a custom state provider. In order for this class
14338  * to be useful, it must be initialized with a provider when your application initializes.
14339  <pre><code>
14340 // in your initialization function
14341 init : function(){
14342    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14343    ...
14344    // supposed you have a {@link Roo.BorderLayout}
14345    var layout = new Roo.BorderLayout(...);
14346    layout.restoreState();
14347    // or a {Roo.BasicDialog}
14348    var dialog = new Roo.BasicDialog(...);
14349    dialog.restoreState();
14350  </code></pre>
14351  * @singleton
14352  */
14353 Roo.state.Manager = function(){
14354     var provider = new Roo.state.Provider();
14355     
14356     return {
14357         /**
14358          * Configures the default state provider for your application
14359          * @param {Provider} stateProvider The state provider to set
14360          */
14361         setProvider : function(stateProvider){
14362             provider = stateProvider;
14363         },
14364         
14365         /**
14366          * Returns the current value for a key
14367          * @param {String} name The key name
14368          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
14369          * @return {Mixed} The state data
14370          */
14371         get : function(key, defaultValue){
14372             return provider.get(key, defaultValue);
14373         },
14374         
14375         /**
14376          * Sets the value for a key
14377          * @param {String} name The key name
14378          * @param {Mixed} value The state data
14379          */
14380          set : function(key, value){
14381             provider.set(key, value);
14382         },
14383         
14384         /**
14385          * Clears a value from the state
14386          * @param {String} name The key name
14387          */
14388         clear : function(key){
14389             provider.clear(key);
14390         },
14391         
14392         /**
14393          * Gets the currently configured state provider
14394          * @return {Provider} The state provider
14395          */
14396         getProvider : function(){
14397             return provider;
14398         }
14399     };
14400 }();
14401 /*
14402  * Based on:
14403  * Ext JS Library 1.1.1
14404  * Copyright(c) 2006-2007, Ext JS, LLC.
14405  *
14406  * Originally Released Under LGPL - original licence link has changed is not relivant.
14407  *
14408  * Fork - LGPL
14409  * <script type="text/javascript">
14410  */
14411 /**
14412  * @class Roo.state.CookieProvider
14413  * @extends Roo.state.Provider
14414  * The default Provider implementation which saves state via cookies.
14415  * <br />Usage:
14416  <pre><code>
14417    var cp = new Roo.state.CookieProvider({
14418        path: "/cgi-bin/",
14419        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
14420        domain: "roojs.com"
14421    })
14422    Roo.state.Manager.setProvider(cp);
14423  </code></pre>
14424  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
14425  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
14426  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
14427  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
14428  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
14429  * domain the page is running on including the 'www' like 'www.roojs.com')
14430  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
14431  * @constructor
14432  * Create a new CookieProvider
14433  * @param {Object} config The configuration object
14434  */
14435 Roo.state.CookieProvider = function(config){
14436     Roo.state.CookieProvider.superclass.constructor.call(this);
14437     this.path = "/";
14438     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
14439     this.domain = null;
14440     this.secure = false;
14441     Roo.apply(this, config);
14442     this.state = this.readCookies();
14443 };
14444
14445 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
14446     // private
14447     set : function(name, value){
14448         if(typeof value == "undefined" || value === null){
14449             this.clear(name);
14450             return;
14451         }
14452         this.setCookie(name, value);
14453         Roo.state.CookieProvider.superclass.set.call(this, name, value);
14454     },
14455
14456     // private
14457     clear : function(name){
14458         this.clearCookie(name);
14459         Roo.state.CookieProvider.superclass.clear.call(this, name);
14460     },
14461
14462     // private
14463     readCookies : function(){
14464         var cookies = {};
14465         var c = document.cookie + ";";
14466         var re = /\s?(.*?)=(.*?);/g;
14467         var matches;
14468         while((matches = re.exec(c)) != null){
14469             var name = matches[1];
14470             var value = matches[2];
14471             if(name && name.substring(0,3) == "ys-"){
14472                 cookies[name.substr(3)] = this.decodeValue(value);
14473             }
14474         }
14475         return cookies;
14476     },
14477
14478     // private
14479     setCookie : function(name, value){
14480         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
14481            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
14482            ((this.path == null) ? "" : ("; path=" + this.path)) +
14483            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14484            ((this.secure == true) ? "; secure" : "");
14485     },
14486
14487     // private
14488     clearCookie : function(name){
14489         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
14490            ((this.path == null) ? "" : ("; path=" + this.path)) +
14491            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14492            ((this.secure == true) ? "; secure" : "");
14493     }
14494 });/*
14495  * Based on:
14496  * Ext JS Library 1.1.1
14497  * Copyright(c) 2006-2007, Ext JS, LLC.
14498  *
14499  * Originally Released Under LGPL - original licence link has changed is not relivant.
14500  *
14501  * Fork - LGPL
14502  * <script type="text/javascript">
14503  */
14504
14505
14506
14507 /*
14508  * These classes are derivatives of the similarly named classes in the YUI Library.
14509  * The original license:
14510  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
14511  * Code licensed under the BSD License:
14512  * http://developer.yahoo.net/yui/license.txt
14513  */
14514
14515 (function() {
14516
14517 var Event=Roo.EventManager;
14518 var Dom=Roo.lib.Dom;
14519
14520 /**
14521  * @class Roo.dd.DragDrop
14522  * @extends Roo.util.Observable
14523  * Defines the interface and base operation of items that that can be
14524  * dragged or can be drop targets.  It was designed to be extended, overriding
14525  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
14526  * Up to three html elements can be associated with a DragDrop instance:
14527  * <ul>
14528  * <li>linked element: the element that is passed into the constructor.
14529  * This is the element which defines the boundaries for interaction with
14530  * other DragDrop objects.</li>
14531  * <li>handle element(s): The drag operation only occurs if the element that
14532  * was clicked matches a handle element.  By default this is the linked
14533  * element, but there are times that you will want only a portion of the
14534  * linked element to initiate the drag operation, and the setHandleElId()
14535  * method provides a way to define this.</li>
14536  * <li>drag element: this represents the element that would be moved along
14537  * with the cursor during a drag operation.  By default, this is the linked
14538  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
14539  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
14540  * </li>
14541  * </ul>
14542  * This class should not be instantiated until the onload event to ensure that
14543  * the associated elements are available.
14544  * The following would define a DragDrop obj that would interact with any
14545  * other DragDrop obj in the "group1" group:
14546  * <pre>
14547  *  dd = new Roo.dd.DragDrop("div1", "group1");
14548  * </pre>
14549  * Since none of the event handlers have been implemented, nothing would
14550  * actually happen if you were to run the code above.  Normally you would
14551  * override this class or one of the default implementations, but you can
14552  * also override the methods you want on an instance of the class...
14553  * <pre>
14554  *  dd.onDragDrop = function(e, id) {
14555  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
14556  *  }
14557  * </pre>
14558  * @constructor
14559  * @param {String} id of the element that is linked to this instance
14560  * @param {String} sGroup the group of related DragDrop objects
14561  * @param {object} config an object containing configurable attributes
14562  *                Valid properties for DragDrop:
14563  *                    padding, isTarget, maintainOffset, primaryButtonOnly
14564  */
14565 Roo.dd.DragDrop = function(id, sGroup, config) {
14566     if (id) {
14567         this.init(id, sGroup, config);
14568     }
14569     
14570 };
14571
14572 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
14573
14574     /**
14575      * The id of the element associated with this object.  This is what we
14576      * refer to as the "linked element" because the size and position of
14577      * this element is used to determine when the drag and drop objects have
14578      * interacted.
14579      * @property id
14580      * @type String
14581      */
14582     id: null,
14583
14584     /**
14585      * Configuration attributes passed into the constructor
14586      * @property config
14587      * @type object
14588      */
14589     config: null,
14590
14591     /**
14592      * The id of the element that will be dragged.  By default this is same
14593      * as the linked element , but could be changed to another element. Ex:
14594      * Roo.dd.DDProxy
14595      * @property dragElId
14596      * @type String
14597      * @private
14598      */
14599     dragElId: null,
14600
14601     /**
14602      * the id of the element that initiates the drag operation.  By default
14603      * this is the linked element, but could be changed to be a child of this
14604      * element.  This lets us do things like only starting the drag when the
14605      * header element within the linked html element is clicked.
14606      * @property handleElId
14607      * @type String
14608      * @private
14609      */
14610     handleElId: null,
14611
14612     /**
14613      * An associative array of HTML tags that will be ignored if clicked.
14614      * @property invalidHandleTypes
14615      * @type {string: string}
14616      */
14617     invalidHandleTypes: null,
14618
14619     /**
14620      * An associative array of ids for elements that will be ignored if clicked
14621      * @property invalidHandleIds
14622      * @type {string: string}
14623      */
14624     invalidHandleIds: null,
14625
14626     /**
14627      * An indexted array of css class names for elements that will be ignored
14628      * if clicked.
14629      * @property invalidHandleClasses
14630      * @type string[]
14631      */
14632     invalidHandleClasses: null,
14633
14634     /**
14635      * The linked element's absolute X position at the time the drag was
14636      * started
14637      * @property startPageX
14638      * @type int
14639      * @private
14640      */
14641     startPageX: 0,
14642
14643     /**
14644      * The linked element's absolute X position at the time the drag was
14645      * started
14646      * @property startPageY
14647      * @type int
14648      * @private
14649      */
14650     startPageY: 0,
14651
14652     /**
14653      * The group defines a logical collection of DragDrop objects that are
14654      * related.  Instances only get events when interacting with other
14655      * DragDrop object in the same group.  This lets us define multiple
14656      * groups using a single DragDrop subclass if we want.
14657      * @property groups
14658      * @type {string: string}
14659      */
14660     groups: null,
14661
14662     /**
14663      * Individual drag/drop instances can be locked.  This will prevent
14664      * onmousedown start drag.
14665      * @property locked
14666      * @type boolean
14667      * @private
14668      */
14669     locked: false,
14670
14671     /**
14672      * Lock this instance
14673      * @method lock
14674      */
14675     lock: function() { this.locked = true; },
14676
14677     /**
14678      * Unlock this instace
14679      * @method unlock
14680      */
14681     unlock: function() { this.locked = false; },
14682
14683     /**
14684      * By default, all insances can be a drop target.  This can be disabled by
14685      * setting isTarget to false.
14686      * @method isTarget
14687      * @type boolean
14688      */
14689     isTarget: true,
14690
14691     /**
14692      * The padding configured for this drag and drop object for calculating
14693      * the drop zone intersection with this object.
14694      * @method padding
14695      * @type int[]
14696      */
14697     padding: null,
14698
14699     /**
14700      * Cached reference to the linked element
14701      * @property _domRef
14702      * @private
14703      */
14704     _domRef: null,
14705
14706     /**
14707      * Internal typeof flag
14708      * @property __ygDragDrop
14709      * @private
14710      */
14711     __ygDragDrop: true,
14712
14713     /**
14714      * Set to true when horizontal contraints are applied
14715      * @property constrainX
14716      * @type boolean
14717      * @private
14718      */
14719     constrainX: false,
14720
14721     /**
14722      * Set to true when vertical contraints are applied
14723      * @property constrainY
14724      * @type boolean
14725      * @private
14726      */
14727     constrainY: false,
14728
14729     /**
14730      * The left constraint
14731      * @property minX
14732      * @type int
14733      * @private
14734      */
14735     minX: 0,
14736
14737     /**
14738      * The right constraint
14739      * @property maxX
14740      * @type int
14741      * @private
14742      */
14743     maxX: 0,
14744
14745     /**
14746      * The up constraint
14747      * @property minY
14748      * @type int
14749      * @type int
14750      * @private
14751      */
14752     minY: 0,
14753
14754     /**
14755      * The down constraint
14756      * @property maxY
14757      * @type int
14758      * @private
14759      */
14760     maxY: 0,
14761
14762     /**
14763      * Maintain offsets when we resetconstraints.  Set to true when you want
14764      * the position of the element relative to its parent to stay the same
14765      * when the page changes
14766      *
14767      * @property maintainOffset
14768      * @type boolean
14769      */
14770     maintainOffset: false,
14771
14772     /**
14773      * Array of pixel locations the element will snap to if we specified a
14774      * horizontal graduation/interval.  This array is generated automatically
14775      * when you define a tick interval.
14776      * @property xTicks
14777      * @type int[]
14778      */
14779     xTicks: null,
14780
14781     /**
14782      * Array of pixel locations the element will snap to if we specified a
14783      * vertical graduation/interval.  This array is generated automatically
14784      * when you define a tick interval.
14785      * @property yTicks
14786      * @type int[]
14787      */
14788     yTicks: null,
14789
14790     /**
14791      * By default the drag and drop instance will only respond to the primary
14792      * button click (left button for a right-handed mouse).  Set to true to
14793      * allow drag and drop to start with any mouse click that is propogated
14794      * by the browser
14795      * @property primaryButtonOnly
14796      * @type boolean
14797      */
14798     primaryButtonOnly: true,
14799
14800     /**
14801      * The availabe property is false until the linked dom element is accessible.
14802      * @property available
14803      * @type boolean
14804      */
14805     available: false,
14806
14807     /**
14808      * By default, drags can only be initiated if the mousedown occurs in the
14809      * region the linked element is.  This is done in part to work around a
14810      * bug in some browsers that mis-report the mousedown if the previous
14811      * mouseup happened outside of the window.  This property is set to true
14812      * if outer handles are defined.
14813      *
14814      * @property hasOuterHandles
14815      * @type boolean
14816      * @default false
14817      */
14818     hasOuterHandles: false,
14819
14820     /**
14821      * Code that executes immediately before the startDrag event
14822      * @method b4StartDrag
14823      * @private
14824      */
14825     b4StartDrag: function(x, y) { },
14826
14827     /**
14828      * Abstract method called after a drag/drop object is clicked
14829      * and the drag or mousedown time thresholds have beeen met.
14830      * @method startDrag
14831      * @param {int} X click location
14832      * @param {int} Y click location
14833      */
14834     startDrag: function(x, y) { /* override this */ },
14835
14836     /**
14837      * Code that executes immediately before the onDrag event
14838      * @method b4Drag
14839      * @private
14840      */
14841     b4Drag: function(e) { },
14842
14843     /**
14844      * Abstract method called during the onMouseMove event while dragging an
14845      * object.
14846      * @method onDrag
14847      * @param {Event} e the mousemove event
14848      */
14849     onDrag: function(e) { /* override this */ },
14850
14851     /**
14852      * Abstract method called when this element fist begins hovering over
14853      * another DragDrop obj
14854      * @method onDragEnter
14855      * @param {Event} e the mousemove event
14856      * @param {String|DragDrop[]} id In POINT mode, the element
14857      * id this is hovering over.  In INTERSECT mode, an array of one or more
14858      * dragdrop items being hovered over.
14859      */
14860     onDragEnter: function(e, id) { /* override this */ },
14861
14862     /**
14863      * Code that executes immediately before the onDragOver event
14864      * @method b4DragOver
14865      * @private
14866      */
14867     b4DragOver: function(e) { },
14868
14869     /**
14870      * Abstract method called when this element is hovering over another
14871      * DragDrop obj
14872      * @method onDragOver
14873      * @param {Event} e the mousemove event
14874      * @param {String|DragDrop[]} id In POINT mode, the element
14875      * id this is hovering over.  In INTERSECT mode, an array of dd items
14876      * being hovered over.
14877      */
14878     onDragOver: function(e, id) { /* override this */ },
14879
14880     /**
14881      * Code that executes immediately before the onDragOut event
14882      * @method b4DragOut
14883      * @private
14884      */
14885     b4DragOut: function(e) { },
14886
14887     /**
14888      * Abstract method called when we are no longer hovering over an element
14889      * @method onDragOut
14890      * @param {Event} e the mousemove event
14891      * @param {String|DragDrop[]} id In POINT mode, the element
14892      * id this was hovering over.  In INTERSECT mode, an array of dd items
14893      * that the mouse is no longer over.
14894      */
14895     onDragOut: function(e, id) { /* override this */ },
14896
14897     /**
14898      * Code that executes immediately before the onDragDrop event
14899      * @method b4DragDrop
14900      * @private
14901      */
14902     b4DragDrop: function(e) { },
14903
14904     /**
14905      * Abstract method called when this item is dropped on another DragDrop
14906      * obj
14907      * @method onDragDrop
14908      * @param {Event} e the mouseup event
14909      * @param {String|DragDrop[]} id In POINT mode, the element
14910      * id this was dropped on.  In INTERSECT mode, an array of dd items this
14911      * was dropped on.
14912      */
14913     onDragDrop: function(e, id) { /* override this */ },
14914
14915     /**
14916      * Abstract method called when this item is dropped on an area with no
14917      * drop target
14918      * @method onInvalidDrop
14919      * @param {Event} e the mouseup event
14920      */
14921     onInvalidDrop: function(e) { /* override this */ },
14922
14923     /**
14924      * Code that executes immediately before the endDrag event
14925      * @method b4EndDrag
14926      * @private
14927      */
14928     b4EndDrag: function(e) { },
14929
14930     /**
14931      * Fired when we are done dragging the object
14932      * @method endDrag
14933      * @param {Event} e the mouseup event
14934      */
14935     endDrag: function(e) { /* override this */ },
14936
14937     /**
14938      * Code executed immediately before the onMouseDown event
14939      * @method b4MouseDown
14940      * @param {Event} e the mousedown event
14941      * @private
14942      */
14943     b4MouseDown: function(e) {  },
14944
14945     /**
14946      * Event handler that fires when a drag/drop obj gets a mousedown
14947      * @method onMouseDown
14948      * @param {Event} e the mousedown event
14949      */
14950     onMouseDown: function(e) { /* override this */ },
14951
14952     /**
14953      * Event handler that fires when a drag/drop obj gets a mouseup
14954      * @method onMouseUp
14955      * @param {Event} e the mouseup event
14956      */
14957     onMouseUp: function(e) { /* override this */ },
14958
14959     /**
14960      * Override the onAvailable method to do what is needed after the initial
14961      * position was determined.
14962      * @method onAvailable
14963      */
14964     onAvailable: function () {
14965     },
14966
14967     /*
14968      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
14969      * @type Object
14970      */
14971     defaultPadding : {left:0, right:0, top:0, bottom:0},
14972
14973     /*
14974      * Initializes the drag drop object's constraints to restrict movement to a certain element.
14975  *
14976  * Usage:
14977  <pre><code>
14978  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
14979                 { dragElId: "existingProxyDiv" });
14980  dd.startDrag = function(){
14981      this.constrainTo("parent-id");
14982  };
14983  </code></pre>
14984  * Or you can initalize it using the {@link Roo.Element} object:
14985  <pre><code>
14986  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
14987      startDrag : function(){
14988          this.constrainTo("parent-id");
14989      }
14990  });
14991  </code></pre>
14992      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
14993      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
14994      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
14995      * an object containing the sides to pad. For example: {right:10, bottom:10}
14996      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
14997      */
14998     constrainTo : function(constrainTo, pad, inContent){
14999         if(typeof pad == "number"){
15000             pad = {left: pad, right:pad, top:pad, bottom:pad};
15001         }
15002         pad = pad || this.defaultPadding;
15003         var b = Roo.get(this.getEl()).getBox();
15004         var ce = Roo.get(constrainTo);
15005         var s = ce.getScroll();
15006         var c, cd = ce.dom;
15007         if(cd == document.body){
15008             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
15009         }else{
15010             xy = ce.getXY();
15011             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
15012         }
15013
15014
15015         var topSpace = b.y - c.y;
15016         var leftSpace = b.x - c.x;
15017
15018         this.resetConstraints();
15019         this.setXConstraint(leftSpace - (pad.left||0), // left
15020                 c.width - leftSpace - b.width - (pad.right||0) //right
15021         );
15022         this.setYConstraint(topSpace - (pad.top||0), //top
15023                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
15024         );
15025     },
15026
15027     /**
15028      * Returns a reference to the linked element
15029      * @method getEl
15030      * @return {HTMLElement} the html element
15031      */
15032     getEl: function() {
15033         if (!this._domRef) {
15034             this._domRef = Roo.getDom(this.id);
15035         }
15036
15037         return this._domRef;
15038     },
15039
15040     /**
15041      * Returns a reference to the actual element to drag.  By default this is
15042      * the same as the html element, but it can be assigned to another
15043      * element. An example of this can be found in Roo.dd.DDProxy
15044      * @method getDragEl
15045      * @return {HTMLElement} the html element
15046      */
15047     getDragEl: function() {
15048         return Roo.getDom(this.dragElId);
15049     },
15050
15051     /**
15052      * Sets up the DragDrop object.  Must be called in the constructor of any
15053      * Roo.dd.DragDrop subclass
15054      * @method init
15055      * @param id the id of the linked element
15056      * @param {String} sGroup the group of related items
15057      * @param {object} config configuration attributes
15058      */
15059     init: function(id, sGroup, config) {
15060         this.initTarget(id, sGroup, config);
15061         Event.on(this.id, "mousedown", this.handleMouseDown, this);
15062         // Event.on(this.id, "selectstart", Event.preventDefault);
15063     },
15064
15065     /**
15066      * Initializes Targeting functionality only... the object does not
15067      * get a mousedown handler.
15068      * @method initTarget
15069      * @param id the id of the linked element
15070      * @param {String} sGroup the group of related items
15071      * @param {object} config configuration attributes
15072      */
15073     initTarget: function(id, sGroup, config) {
15074
15075         // configuration attributes
15076         this.config = config || {};
15077
15078         // create a local reference to the drag and drop manager
15079         this.DDM = Roo.dd.DDM;
15080         // initialize the groups array
15081         this.groups = {};
15082
15083         // assume that we have an element reference instead of an id if the
15084         // parameter is not a string
15085         if (typeof id !== "string") {
15086             id = Roo.id(id);
15087         }
15088
15089         // set the id
15090         this.id = id;
15091
15092         // add to an interaction group
15093         this.addToGroup((sGroup) ? sGroup : "default");
15094
15095         // We don't want to register this as the handle with the manager
15096         // so we just set the id rather than calling the setter.
15097         this.handleElId = id;
15098
15099         // the linked element is the element that gets dragged by default
15100         this.setDragElId(id);
15101
15102         // by default, clicked anchors will not start drag operations.
15103         this.invalidHandleTypes = { A: "A" };
15104         this.invalidHandleIds = {};
15105         this.invalidHandleClasses = [];
15106
15107         this.applyConfig();
15108
15109         this.handleOnAvailable();
15110     },
15111
15112     /**
15113      * Applies the configuration parameters that were passed into the constructor.
15114      * This is supposed to happen at each level through the inheritance chain.  So
15115      * a DDProxy implentation will execute apply config on DDProxy, DD, and
15116      * DragDrop in order to get all of the parameters that are available in
15117      * each object.
15118      * @method applyConfig
15119      */
15120     applyConfig: function() {
15121
15122         // configurable properties:
15123         //    padding, isTarget, maintainOffset, primaryButtonOnly
15124         this.padding           = this.config.padding || [0, 0, 0, 0];
15125         this.isTarget          = (this.config.isTarget !== false);
15126         this.maintainOffset    = (this.config.maintainOffset);
15127         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
15128
15129     },
15130
15131     /**
15132      * Executed when the linked element is available
15133      * @method handleOnAvailable
15134      * @private
15135      */
15136     handleOnAvailable: function() {
15137         this.available = true;
15138         this.resetConstraints();
15139         this.onAvailable();
15140     },
15141
15142      /**
15143      * Configures the padding for the target zone in px.  Effectively expands
15144      * (or reduces) the virtual object size for targeting calculations.
15145      * Supports css-style shorthand; if only one parameter is passed, all sides
15146      * will have that padding, and if only two are passed, the top and bottom
15147      * will have the first param, the left and right the second.
15148      * @method setPadding
15149      * @param {int} iTop    Top pad
15150      * @param {int} iRight  Right pad
15151      * @param {int} iBot    Bot pad
15152      * @param {int} iLeft   Left pad
15153      */
15154     setPadding: function(iTop, iRight, iBot, iLeft) {
15155         // this.padding = [iLeft, iRight, iTop, iBot];
15156         if (!iRight && 0 !== iRight) {
15157             this.padding = [iTop, iTop, iTop, iTop];
15158         } else if (!iBot && 0 !== iBot) {
15159             this.padding = [iTop, iRight, iTop, iRight];
15160         } else {
15161             this.padding = [iTop, iRight, iBot, iLeft];
15162         }
15163     },
15164
15165     /**
15166      * Stores the initial placement of the linked element.
15167      * @method setInitialPosition
15168      * @param {int} diffX   the X offset, default 0
15169      * @param {int} diffY   the Y offset, default 0
15170      */
15171     setInitPosition: function(diffX, diffY) {
15172         var el = this.getEl();
15173
15174         if (!this.DDM.verifyEl(el)) {
15175             return;
15176         }
15177
15178         var dx = diffX || 0;
15179         var dy = diffY || 0;
15180
15181         var p = Dom.getXY( el );
15182
15183         this.initPageX = p[0] - dx;
15184         this.initPageY = p[1] - dy;
15185
15186         this.lastPageX = p[0];
15187         this.lastPageY = p[1];
15188
15189
15190         this.setStartPosition(p);
15191     },
15192
15193     /**
15194      * Sets the start position of the element.  This is set when the obj
15195      * is initialized, the reset when a drag is started.
15196      * @method setStartPosition
15197      * @param pos current position (from previous lookup)
15198      * @private
15199      */
15200     setStartPosition: function(pos) {
15201         var p = pos || Dom.getXY( this.getEl() );
15202         this.deltaSetXY = null;
15203
15204         this.startPageX = p[0];
15205         this.startPageY = p[1];
15206     },
15207
15208     /**
15209      * Add this instance to a group of related drag/drop objects.  All
15210      * instances belong to at least one group, and can belong to as many
15211      * groups as needed.
15212      * @method addToGroup
15213      * @param sGroup {string} the name of the group
15214      */
15215     addToGroup: function(sGroup) {
15216         this.groups[sGroup] = true;
15217         this.DDM.regDragDrop(this, sGroup);
15218     },
15219
15220     /**
15221      * Remove's this instance from the supplied interaction group
15222      * @method removeFromGroup
15223      * @param {string}  sGroup  The group to drop
15224      */
15225     removeFromGroup: function(sGroup) {
15226         if (this.groups[sGroup]) {
15227             delete this.groups[sGroup];
15228         }
15229
15230         this.DDM.removeDDFromGroup(this, sGroup);
15231     },
15232
15233     /**
15234      * Allows you to specify that an element other than the linked element
15235      * will be moved with the cursor during a drag
15236      * @method setDragElId
15237      * @param id {string} the id of the element that will be used to initiate the drag
15238      */
15239     setDragElId: function(id) {
15240         this.dragElId = id;
15241     },
15242
15243     /**
15244      * Allows you to specify a child of the linked element that should be
15245      * used to initiate the drag operation.  An example of this would be if
15246      * you have a content div with text and links.  Clicking anywhere in the
15247      * content area would normally start the drag operation.  Use this method
15248      * to specify that an element inside of the content div is the element
15249      * that starts the drag operation.
15250      * @method setHandleElId
15251      * @param id {string} the id of the element that will be used to
15252      * initiate the drag.
15253      */
15254     setHandleElId: function(id) {
15255         if (typeof id !== "string") {
15256             id = Roo.id(id);
15257         }
15258         this.handleElId = id;
15259         this.DDM.regHandle(this.id, id);
15260     },
15261
15262     /**
15263      * Allows you to set an element outside of the linked element as a drag
15264      * handle
15265      * @method setOuterHandleElId
15266      * @param id the id of the element that will be used to initiate the drag
15267      */
15268     setOuterHandleElId: function(id) {
15269         if (typeof id !== "string") {
15270             id = Roo.id(id);
15271         }
15272         Event.on(id, "mousedown",
15273                 this.handleMouseDown, this);
15274         this.setHandleElId(id);
15275
15276         this.hasOuterHandles = true;
15277     },
15278
15279     /**
15280      * Remove all drag and drop hooks for this element
15281      * @method unreg
15282      */
15283     unreg: function() {
15284         Event.un(this.id, "mousedown",
15285                 this.handleMouseDown);
15286         this._domRef = null;
15287         this.DDM._remove(this);
15288     },
15289
15290     destroy : function(){
15291         this.unreg();
15292     },
15293
15294     /**
15295      * Returns true if this instance is locked, or the drag drop mgr is locked
15296      * (meaning that all drag/drop is disabled on the page.)
15297      * @method isLocked
15298      * @return {boolean} true if this obj or all drag/drop is locked, else
15299      * false
15300      */
15301     isLocked: function() {
15302         return (this.DDM.isLocked() || this.locked);
15303     },
15304
15305     /**
15306      * Fired when this object is clicked
15307      * @method handleMouseDown
15308      * @param {Event} e
15309      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
15310      * @private
15311      */
15312     handleMouseDown: function(e, oDD){
15313         if (this.primaryButtonOnly && e.button != 0) {
15314             return;
15315         }
15316
15317         if (this.isLocked()) {
15318             return;
15319         }
15320
15321         this.DDM.refreshCache(this.groups);
15322
15323         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
15324         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
15325         } else {
15326             if (this.clickValidator(e)) {
15327
15328                 // set the initial element position
15329                 this.setStartPosition();
15330
15331
15332                 this.b4MouseDown(e);
15333                 this.onMouseDown(e);
15334
15335                 this.DDM.handleMouseDown(e, this);
15336
15337                 this.DDM.stopEvent(e);
15338             } else {
15339
15340
15341             }
15342         }
15343     },
15344
15345     clickValidator: function(e) {
15346         var target = e.getTarget();
15347         return ( this.isValidHandleChild(target) &&
15348                     (this.id == this.handleElId ||
15349                         this.DDM.handleWasClicked(target, this.id)) );
15350     },
15351
15352     /**
15353      * Allows you to specify a tag name that should not start a drag operation
15354      * when clicked.  This is designed to facilitate embedding links within a
15355      * drag handle that do something other than start the drag.
15356      * @method addInvalidHandleType
15357      * @param {string} tagName the type of element to exclude
15358      */
15359     addInvalidHandleType: function(tagName) {
15360         var type = tagName.toUpperCase();
15361         this.invalidHandleTypes[type] = type;
15362     },
15363
15364     /**
15365      * Lets you to specify an element id for a child of a drag handle
15366      * that should not initiate a drag
15367      * @method addInvalidHandleId
15368      * @param {string} id the element id of the element you wish to ignore
15369      */
15370     addInvalidHandleId: function(id) {
15371         if (typeof id !== "string") {
15372             id = Roo.id(id);
15373         }
15374         this.invalidHandleIds[id] = id;
15375     },
15376
15377     /**
15378      * Lets you specify a css class of elements that will not initiate a drag
15379      * @method addInvalidHandleClass
15380      * @param {string} cssClass the class of the elements you wish to ignore
15381      */
15382     addInvalidHandleClass: function(cssClass) {
15383         this.invalidHandleClasses.push(cssClass);
15384     },
15385
15386     /**
15387      * Unsets an excluded tag name set by addInvalidHandleType
15388      * @method removeInvalidHandleType
15389      * @param {string} tagName the type of element to unexclude
15390      */
15391     removeInvalidHandleType: function(tagName) {
15392         var type = tagName.toUpperCase();
15393         // this.invalidHandleTypes[type] = null;
15394         delete this.invalidHandleTypes[type];
15395     },
15396
15397     /**
15398      * Unsets an invalid handle id
15399      * @method removeInvalidHandleId
15400      * @param {string} id the id of the element to re-enable
15401      */
15402     removeInvalidHandleId: function(id) {
15403         if (typeof id !== "string") {
15404             id = Roo.id(id);
15405         }
15406         delete this.invalidHandleIds[id];
15407     },
15408
15409     /**
15410      * Unsets an invalid css class
15411      * @method removeInvalidHandleClass
15412      * @param {string} cssClass the class of the element(s) you wish to
15413      * re-enable
15414      */
15415     removeInvalidHandleClass: function(cssClass) {
15416         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
15417             if (this.invalidHandleClasses[i] == cssClass) {
15418                 delete this.invalidHandleClasses[i];
15419             }
15420         }
15421     },
15422
15423     /**
15424      * Checks the tag exclusion list to see if this click should be ignored
15425      * @method isValidHandleChild
15426      * @param {HTMLElement} node the HTMLElement to evaluate
15427      * @return {boolean} true if this is a valid tag type, false if not
15428      */
15429     isValidHandleChild: function(node) {
15430
15431         var valid = true;
15432         // var n = (node.nodeName == "#text") ? node.parentNode : node;
15433         var nodeName;
15434         try {
15435             nodeName = node.nodeName.toUpperCase();
15436         } catch(e) {
15437             nodeName = node.nodeName;
15438         }
15439         valid = valid && !this.invalidHandleTypes[nodeName];
15440         valid = valid && !this.invalidHandleIds[node.id];
15441
15442         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
15443             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
15444         }
15445
15446
15447         return valid;
15448
15449     },
15450
15451     /**
15452      * Create the array of horizontal tick marks if an interval was specified
15453      * in setXConstraint().
15454      * @method setXTicks
15455      * @private
15456      */
15457     setXTicks: function(iStartX, iTickSize) {
15458         this.xTicks = [];
15459         this.xTickSize = iTickSize;
15460
15461         var tickMap = {};
15462
15463         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
15464             if (!tickMap[i]) {
15465                 this.xTicks[this.xTicks.length] = i;
15466                 tickMap[i] = true;
15467             }
15468         }
15469
15470         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
15471             if (!tickMap[i]) {
15472                 this.xTicks[this.xTicks.length] = i;
15473                 tickMap[i] = true;
15474             }
15475         }
15476
15477         this.xTicks.sort(this.DDM.numericSort) ;
15478     },
15479
15480     /**
15481      * Create the array of vertical tick marks if an interval was specified in
15482      * setYConstraint().
15483      * @method setYTicks
15484      * @private
15485      */
15486     setYTicks: function(iStartY, iTickSize) {
15487         this.yTicks = [];
15488         this.yTickSize = iTickSize;
15489
15490         var tickMap = {};
15491
15492         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
15493             if (!tickMap[i]) {
15494                 this.yTicks[this.yTicks.length] = i;
15495                 tickMap[i] = true;
15496             }
15497         }
15498
15499         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
15500             if (!tickMap[i]) {
15501                 this.yTicks[this.yTicks.length] = i;
15502                 tickMap[i] = true;
15503             }
15504         }
15505
15506         this.yTicks.sort(this.DDM.numericSort) ;
15507     },
15508
15509     /**
15510      * By default, the element can be dragged any place on the screen.  Use
15511      * this method to limit the horizontal travel of the element.  Pass in
15512      * 0,0 for the parameters if you want to lock the drag to the y axis.
15513      * @method setXConstraint
15514      * @param {int} iLeft the number of pixels the element can move to the left
15515      * @param {int} iRight the number of pixels the element can move to the
15516      * right
15517      * @param {int} iTickSize optional parameter for specifying that the
15518      * element
15519      * should move iTickSize pixels at a time.
15520      */
15521     setXConstraint: function(iLeft, iRight, iTickSize) {
15522         this.leftConstraint = iLeft;
15523         this.rightConstraint = iRight;
15524
15525         this.minX = this.initPageX - iLeft;
15526         this.maxX = this.initPageX + iRight;
15527         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
15528
15529         this.constrainX = true;
15530     },
15531
15532     /**
15533      * Clears any constraints applied to this instance.  Also clears ticks
15534      * since they can't exist independent of a constraint at this time.
15535      * @method clearConstraints
15536      */
15537     clearConstraints: function() {
15538         this.constrainX = false;
15539         this.constrainY = false;
15540         this.clearTicks();
15541     },
15542
15543     /**
15544      * Clears any tick interval defined for this instance
15545      * @method clearTicks
15546      */
15547     clearTicks: function() {
15548         this.xTicks = null;
15549         this.yTicks = null;
15550         this.xTickSize = 0;
15551         this.yTickSize = 0;
15552     },
15553
15554     /**
15555      * By default, the element can be dragged any place on the screen.  Set
15556      * this to limit the vertical travel of the element.  Pass in 0,0 for the
15557      * parameters if you want to lock the drag to the x axis.
15558      * @method setYConstraint
15559      * @param {int} iUp the number of pixels the element can move up
15560      * @param {int} iDown the number of pixels the element can move down
15561      * @param {int} iTickSize optional parameter for specifying that the
15562      * element should move iTickSize pixels at a time.
15563      */
15564     setYConstraint: function(iUp, iDown, iTickSize) {
15565         this.topConstraint = iUp;
15566         this.bottomConstraint = iDown;
15567
15568         this.minY = this.initPageY - iUp;
15569         this.maxY = this.initPageY + iDown;
15570         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
15571
15572         this.constrainY = true;
15573
15574     },
15575
15576     /**
15577      * resetConstraints must be called if you manually reposition a dd element.
15578      * @method resetConstraints
15579      * @param {boolean} maintainOffset
15580      */
15581     resetConstraints: function() {
15582
15583
15584         // Maintain offsets if necessary
15585         if (this.initPageX || this.initPageX === 0) {
15586             // figure out how much this thing has moved
15587             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
15588             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
15589
15590             this.setInitPosition(dx, dy);
15591
15592         // This is the first time we have detected the element's position
15593         } else {
15594             this.setInitPosition();
15595         }
15596
15597         if (this.constrainX) {
15598             this.setXConstraint( this.leftConstraint,
15599                                  this.rightConstraint,
15600                                  this.xTickSize        );
15601         }
15602
15603         if (this.constrainY) {
15604             this.setYConstraint( this.topConstraint,
15605                                  this.bottomConstraint,
15606                                  this.yTickSize         );
15607         }
15608     },
15609
15610     /**
15611      * Normally the drag element is moved pixel by pixel, but we can specify
15612      * that it move a number of pixels at a time.  This method resolves the
15613      * location when we have it set up like this.
15614      * @method getTick
15615      * @param {int} val where we want to place the object
15616      * @param {int[]} tickArray sorted array of valid points
15617      * @return {int} the closest tick
15618      * @private
15619      */
15620     getTick: function(val, tickArray) {
15621
15622         if (!tickArray) {
15623             // If tick interval is not defined, it is effectively 1 pixel,
15624             // so we return the value passed to us.
15625             return val;
15626         } else if (tickArray[0] >= val) {
15627             // The value is lower than the first tick, so we return the first
15628             // tick.
15629             return tickArray[0];
15630         } else {
15631             for (var i=0, len=tickArray.length; i<len; ++i) {
15632                 var next = i + 1;
15633                 if (tickArray[next] && tickArray[next] >= val) {
15634                     var diff1 = val - tickArray[i];
15635                     var diff2 = tickArray[next] - val;
15636                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
15637                 }
15638             }
15639
15640             // The value is larger than the last tick, so we return the last
15641             // tick.
15642             return tickArray[tickArray.length - 1];
15643         }
15644     },
15645
15646     /**
15647      * toString method
15648      * @method toString
15649      * @return {string} string representation of the dd obj
15650      */
15651     toString: function() {
15652         return ("DragDrop " + this.id);
15653     }
15654
15655 });
15656
15657 })();
15658 /*
15659  * Based on:
15660  * Ext JS Library 1.1.1
15661  * Copyright(c) 2006-2007, Ext JS, LLC.
15662  *
15663  * Originally Released Under LGPL - original licence link has changed is not relivant.
15664  *
15665  * Fork - LGPL
15666  * <script type="text/javascript">
15667  */
15668
15669
15670 /**
15671  * The drag and drop utility provides a framework for building drag and drop
15672  * applications.  In addition to enabling drag and drop for specific elements,
15673  * the drag and drop elements are tracked by the manager class, and the
15674  * interactions between the various elements are tracked during the drag and
15675  * the implementing code is notified about these important moments.
15676  */
15677
15678 // Only load the library once.  Rewriting the manager class would orphan
15679 // existing drag and drop instances.
15680 if (!Roo.dd.DragDropMgr) {
15681
15682 /**
15683  * @class Roo.dd.DragDropMgr
15684  * DragDropMgr is a singleton that tracks the element interaction for
15685  * all DragDrop items in the window.  Generally, you will not call
15686  * this class directly, but it does have helper methods that could
15687  * be useful in your DragDrop implementations.
15688  * @singleton
15689  */
15690 Roo.dd.DragDropMgr = function() {
15691
15692     var Event = Roo.EventManager;
15693
15694     return {
15695
15696         /**
15697          * Two dimensional Array of registered DragDrop objects.  The first
15698          * dimension is the DragDrop item group, the second the DragDrop
15699          * object.
15700          * @property ids
15701          * @type {string: string}
15702          * @private
15703          * @static
15704          */
15705         ids: {},
15706
15707         /**
15708          * Array of element ids defined as drag handles.  Used to determine
15709          * if the element that generated the mousedown event is actually the
15710          * handle and not the html element itself.
15711          * @property handleIds
15712          * @type {string: string}
15713          * @private
15714          * @static
15715          */
15716         handleIds: {},
15717
15718         /**
15719          * the DragDrop object that is currently being dragged
15720          * @property dragCurrent
15721          * @type DragDrop
15722          * @private
15723          * @static
15724          **/
15725         dragCurrent: null,
15726
15727         /**
15728          * the DragDrop object(s) that are being hovered over
15729          * @property dragOvers
15730          * @type Array
15731          * @private
15732          * @static
15733          */
15734         dragOvers: {},
15735
15736         /**
15737          * the X distance between the cursor and the object being dragged
15738          * @property deltaX
15739          * @type int
15740          * @private
15741          * @static
15742          */
15743         deltaX: 0,
15744
15745         /**
15746          * the Y distance between the cursor and the object being dragged
15747          * @property deltaY
15748          * @type int
15749          * @private
15750          * @static
15751          */
15752         deltaY: 0,
15753
15754         /**
15755          * Flag to determine if we should prevent the default behavior of the
15756          * events we define. By default this is true, but this can be set to
15757          * false if you need the default behavior (not recommended)
15758          * @property preventDefault
15759          * @type boolean
15760          * @static
15761          */
15762         preventDefault: true,
15763
15764         /**
15765          * Flag to determine if we should stop the propagation of the events
15766          * we generate. This is true by default but you may want to set it to
15767          * false if the html element contains other features that require the
15768          * mouse click.
15769          * @property stopPropagation
15770          * @type boolean
15771          * @static
15772          */
15773         stopPropagation: true,
15774
15775         /**
15776          * Internal flag that is set to true when drag and drop has been
15777          * intialized
15778          * @property initialized
15779          * @private
15780          * @static
15781          */
15782         initalized: false,
15783
15784         /**
15785          * All drag and drop can be disabled.
15786          * @property locked
15787          * @private
15788          * @static
15789          */
15790         locked: false,
15791
15792         /**
15793          * Called the first time an element is registered.
15794          * @method init
15795          * @private
15796          * @static
15797          */
15798         init: function() {
15799             this.initialized = true;
15800         },
15801
15802         /**
15803          * In point mode, drag and drop interaction is defined by the
15804          * location of the cursor during the drag/drop
15805          * @property POINT
15806          * @type int
15807          * @static
15808          */
15809         POINT: 0,
15810
15811         /**
15812          * In intersect mode, drag and drop interactio nis defined by the
15813          * overlap of two or more drag and drop objects.
15814          * @property INTERSECT
15815          * @type int
15816          * @static
15817          */
15818         INTERSECT: 1,
15819
15820         /**
15821          * The current drag and drop mode.  Default: POINT
15822          * @property mode
15823          * @type int
15824          * @static
15825          */
15826         mode: 0,
15827
15828         /**
15829          * Runs method on all drag and drop objects
15830          * @method _execOnAll
15831          * @private
15832          * @static
15833          */
15834         _execOnAll: function(sMethod, args) {
15835             for (var i in this.ids) {
15836                 for (var j in this.ids[i]) {
15837                     var oDD = this.ids[i][j];
15838                     if (! this.isTypeOfDD(oDD)) {
15839                         continue;
15840                     }
15841                     oDD[sMethod].apply(oDD, args);
15842                 }
15843             }
15844         },
15845
15846         /**
15847          * Drag and drop initialization.  Sets up the global event handlers
15848          * @method _onLoad
15849          * @private
15850          * @static
15851          */
15852         _onLoad: function() {
15853
15854             this.init();
15855
15856
15857             Event.on(document, "mouseup",   this.handleMouseUp, this, true);
15858             Event.on(document, "mousemove", this.handleMouseMove, this, true);
15859             Event.on(window,   "unload",    this._onUnload, this, true);
15860             Event.on(window,   "resize",    this._onResize, this, true);
15861             // Event.on(window,   "mouseout",    this._test);
15862
15863         },
15864
15865         /**
15866          * Reset constraints on all drag and drop objs
15867          * @method _onResize
15868          * @private
15869          * @static
15870          */
15871         _onResize: function(e) {
15872             this._execOnAll("resetConstraints", []);
15873         },
15874
15875         /**
15876          * Lock all drag and drop functionality
15877          * @method lock
15878          * @static
15879          */
15880         lock: function() { this.locked = true; },
15881
15882         /**
15883          * Unlock all drag and drop functionality
15884          * @method unlock
15885          * @static
15886          */
15887         unlock: function() { this.locked = false; },
15888
15889         /**
15890          * Is drag and drop locked?
15891          * @method isLocked
15892          * @return {boolean} True if drag and drop is locked, false otherwise.
15893          * @static
15894          */
15895         isLocked: function() { return this.locked; },
15896
15897         /**
15898          * Location cache that is set for all drag drop objects when a drag is
15899          * initiated, cleared when the drag is finished.
15900          * @property locationCache
15901          * @private
15902          * @static
15903          */
15904         locationCache: {},
15905
15906         /**
15907          * Set useCache to false if you want to force object the lookup of each
15908          * drag and drop linked element constantly during a drag.
15909          * @property useCache
15910          * @type boolean
15911          * @static
15912          */
15913         useCache: true,
15914
15915         /**
15916          * The number of pixels that the mouse needs to move after the
15917          * mousedown before the drag is initiated.  Default=3;
15918          * @property clickPixelThresh
15919          * @type int
15920          * @static
15921          */
15922         clickPixelThresh: 3,
15923
15924         /**
15925          * The number of milliseconds after the mousedown event to initiate the
15926          * drag if we don't get a mouseup event. Default=1000
15927          * @property clickTimeThresh
15928          * @type int
15929          * @static
15930          */
15931         clickTimeThresh: 350,
15932
15933         /**
15934          * Flag that indicates that either the drag pixel threshold or the
15935          * mousdown time threshold has been met
15936          * @property dragThreshMet
15937          * @type boolean
15938          * @private
15939          * @static
15940          */
15941         dragThreshMet: false,
15942
15943         /**
15944          * Timeout used for the click time threshold
15945          * @property clickTimeout
15946          * @type Object
15947          * @private
15948          * @static
15949          */
15950         clickTimeout: null,
15951
15952         /**
15953          * The X position of the mousedown event stored for later use when a
15954          * drag threshold is met.
15955          * @property startX
15956          * @type int
15957          * @private
15958          * @static
15959          */
15960         startX: 0,
15961
15962         /**
15963          * The Y position of the mousedown event stored for later use when a
15964          * drag threshold is met.
15965          * @property startY
15966          * @type int
15967          * @private
15968          * @static
15969          */
15970         startY: 0,
15971
15972         /**
15973          * Each DragDrop instance must be registered with the DragDropMgr.
15974          * This is executed in DragDrop.init()
15975          * @method regDragDrop
15976          * @param {DragDrop} oDD the DragDrop object to register
15977          * @param {String} sGroup the name of the group this element belongs to
15978          * @static
15979          */
15980         regDragDrop: function(oDD, sGroup) {
15981             if (!this.initialized) { this.init(); }
15982
15983             if (!this.ids[sGroup]) {
15984                 this.ids[sGroup] = {};
15985             }
15986             this.ids[sGroup][oDD.id] = oDD;
15987         },
15988
15989         /**
15990          * Removes the supplied dd instance from the supplied group. Executed
15991          * by DragDrop.removeFromGroup, so don't call this function directly.
15992          * @method removeDDFromGroup
15993          * @private
15994          * @static
15995          */
15996         removeDDFromGroup: function(oDD, sGroup) {
15997             if (!this.ids[sGroup]) {
15998                 this.ids[sGroup] = {};
15999             }
16000
16001             var obj = this.ids[sGroup];
16002             if (obj && obj[oDD.id]) {
16003                 delete obj[oDD.id];
16004             }
16005         },
16006
16007         /**
16008          * Unregisters a drag and drop item.  This is executed in
16009          * DragDrop.unreg, use that method instead of calling this directly.
16010          * @method _remove
16011          * @private
16012          * @static
16013          */
16014         _remove: function(oDD) {
16015             for (var g in oDD.groups) {
16016                 if (g && this.ids[g][oDD.id]) {
16017                     delete this.ids[g][oDD.id];
16018                 }
16019             }
16020             delete this.handleIds[oDD.id];
16021         },
16022
16023         /**
16024          * Each DragDrop handle element must be registered.  This is done
16025          * automatically when executing DragDrop.setHandleElId()
16026          * @method regHandle
16027          * @param {String} sDDId the DragDrop id this element is a handle for
16028          * @param {String} sHandleId the id of the element that is the drag
16029          * handle
16030          * @static
16031          */
16032         regHandle: function(sDDId, sHandleId) {
16033             if (!this.handleIds[sDDId]) {
16034                 this.handleIds[sDDId] = {};
16035             }
16036             this.handleIds[sDDId][sHandleId] = sHandleId;
16037         },
16038
16039         /**
16040          * Utility function to determine if a given element has been
16041          * registered as a drag drop item.
16042          * @method isDragDrop
16043          * @param {String} id the element id to check
16044          * @return {boolean} true if this element is a DragDrop item,
16045          * false otherwise
16046          * @static
16047          */
16048         isDragDrop: function(id) {
16049             return ( this.getDDById(id) ) ? true : false;
16050         },
16051
16052         /**
16053          * Returns the drag and drop instances that are in all groups the
16054          * passed in instance belongs to.
16055          * @method getRelated
16056          * @param {DragDrop} p_oDD the obj to get related data for
16057          * @param {boolean} bTargetsOnly if true, only return targetable objs
16058          * @return {DragDrop[]} the related instances
16059          * @static
16060          */
16061         getRelated: function(p_oDD, bTargetsOnly) {
16062             var oDDs = [];
16063             for (var i in p_oDD.groups) {
16064                 for (j in this.ids[i]) {
16065                     var dd = this.ids[i][j];
16066                     if (! this.isTypeOfDD(dd)) {
16067                         continue;
16068                     }
16069                     if (!bTargetsOnly || dd.isTarget) {
16070                         oDDs[oDDs.length] = dd;
16071                     }
16072                 }
16073             }
16074
16075             return oDDs;
16076         },
16077
16078         /**
16079          * Returns true if the specified dd target is a legal target for
16080          * the specifice drag obj
16081          * @method isLegalTarget
16082          * @param {DragDrop} the drag obj
16083          * @param {DragDrop} the target
16084          * @return {boolean} true if the target is a legal target for the
16085          * dd obj
16086          * @static
16087          */
16088         isLegalTarget: function (oDD, oTargetDD) {
16089             var targets = this.getRelated(oDD, true);
16090             for (var i=0, len=targets.length;i<len;++i) {
16091                 if (targets[i].id == oTargetDD.id) {
16092                     return true;
16093                 }
16094             }
16095
16096             return false;
16097         },
16098
16099         /**
16100          * My goal is to be able to transparently determine if an object is
16101          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
16102          * returns "object", oDD.constructor.toString() always returns
16103          * "DragDrop" and not the name of the subclass.  So for now it just
16104          * evaluates a well-known variable in DragDrop.
16105          * @method isTypeOfDD
16106          * @param {Object} the object to evaluate
16107          * @return {boolean} true if typeof oDD = DragDrop
16108          * @static
16109          */
16110         isTypeOfDD: function (oDD) {
16111             return (oDD && oDD.__ygDragDrop);
16112         },
16113
16114         /**
16115          * Utility function to determine if a given element has been
16116          * registered as a drag drop handle for the given Drag Drop object.
16117          * @method isHandle
16118          * @param {String} id the element id to check
16119          * @return {boolean} true if this element is a DragDrop handle, false
16120          * otherwise
16121          * @static
16122          */
16123         isHandle: function(sDDId, sHandleId) {
16124             return ( this.handleIds[sDDId] &&
16125                             this.handleIds[sDDId][sHandleId] );
16126         },
16127
16128         /**
16129          * Returns the DragDrop instance for a given id
16130          * @method getDDById
16131          * @param {String} id the id of the DragDrop object
16132          * @return {DragDrop} the drag drop object, null if it is not found
16133          * @static
16134          */
16135         getDDById: function(id) {
16136             for (var i in this.ids) {
16137                 if (this.ids[i][id]) {
16138                     return this.ids[i][id];
16139                 }
16140             }
16141             return null;
16142         },
16143
16144         /**
16145          * Fired after a registered DragDrop object gets the mousedown event.
16146          * Sets up the events required to track the object being dragged
16147          * @method handleMouseDown
16148          * @param {Event} e the event
16149          * @param oDD the DragDrop object being dragged
16150          * @private
16151          * @static
16152          */
16153         handleMouseDown: function(e, oDD) {
16154             if(Roo.QuickTips){
16155                 Roo.QuickTips.disable();
16156             }
16157             this.currentTarget = e.getTarget();
16158
16159             this.dragCurrent = oDD;
16160
16161             var el = oDD.getEl();
16162
16163             // track start position
16164             this.startX = e.getPageX();
16165             this.startY = e.getPageY();
16166
16167             this.deltaX = this.startX - el.offsetLeft;
16168             this.deltaY = this.startY - el.offsetTop;
16169
16170             this.dragThreshMet = false;
16171
16172             this.clickTimeout = setTimeout(
16173                     function() {
16174                         var DDM = Roo.dd.DDM;
16175                         DDM.startDrag(DDM.startX, DDM.startY);
16176                     },
16177                     this.clickTimeThresh );
16178         },
16179
16180         /**
16181          * Fired when either the drag pixel threshol or the mousedown hold
16182          * time threshold has been met.
16183          * @method startDrag
16184          * @param x {int} the X position of the original mousedown
16185          * @param y {int} the Y position of the original mousedown
16186          * @static
16187          */
16188         startDrag: function(x, y) {
16189             clearTimeout(this.clickTimeout);
16190             if (this.dragCurrent) {
16191                 this.dragCurrent.b4StartDrag(x, y);
16192                 this.dragCurrent.startDrag(x, y);
16193             }
16194             this.dragThreshMet = true;
16195         },
16196
16197         /**
16198          * Internal function to handle the mouseup event.  Will be invoked
16199          * from the context of the document.
16200          * @method handleMouseUp
16201          * @param {Event} e the event
16202          * @private
16203          * @static
16204          */
16205         handleMouseUp: function(e) {
16206
16207             if(Roo.QuickTips){
16208                 Roo.QuickTips.enable();
16209             }
16210             if (! this.dragCurrent) {
16211                 return;
16212             }
16213
16214             clearTimeout(this.clickTimeout);
16215
16216             if (this.dragThreshMet) {
16217                 this.fireEvents(e, true);
16218             } else {
16219             }
16220
16221             this.stopDrag(e);
16222
16223             this.stopEvent(e);
16224         },
16225
16226         /**
16227          * Utility to stop event propagation and event default, if these
16228          * features are turned on.
16229          * @method stopEvent
16230          * @param {Event} e the event as returned by this.getEvent()
16231          * @static
16232          */
16233         stopEvent: function(e){
16234             if(this.stopPropagation) {
16235                 e.stopPropagation();
16236             }
16237
16238             if (this.preventDefault) {
16239                 e.preventDefault();
16240             }
16241         },
16242
16243         /**
16244          * Internal function to clean up event handlers after the drag
16245          * operation is complete
16246          * @method stopDrag
16247          * @param {Event} e the event
16248          * @private
16249          * @static
16250          */
16251         stopDrag: function(e) {
16252             // Fire the drag end event for the item that was dragged
16253             if (this.dragCurrent) {
16254                 if (this.dragThreshMet) {
16255                     this.dragCurrent.b4EndDrag(e);
16256                     this.dragCurrent.endDrag(e);
16257                 }
16258
16259                 this.dragCurrent.onMouseUp(e);
16260             }
16261
16262             this.dragCurrent = null;
16263             this.dragOvers = {};
16264         },
16265
16266         /**
16267          * Internal function to handle the mousemove event.  Will be invoked
16268          * from the context of the html element.
16269          *
16270          * @TODO figure out what we can do about mouse events lost when the
16271          * user drags objects beyond the window boundary.  Currently we can
16272          * detect this in internet explorer by verifying that the mouse is
16273          * down during the mousemove event.  Firefox doesn't give us the
16274          * button state on the mousemove event.
16275          * @method handleMouseMove
16276          * @param {Event} e the event
16277          * @private
16278          * @static
16279          */
16280         handleMouseMove: function(e) {
16281             if (! this.dragCurrent) {
16282                 return true;
16283             }
16284
16285             // var button = e.which || e.button;
16286
16287             // check for IE mouseup outside of page boundary
16288             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
16289                 this.stopEvent(e);
16290                 return this.handleMouseUp(e);
16291             }
16292
16293             if (!this.dragThreshMet) {
16294                 var diffX = Math.abs(this.startX - e.getPageX());
16295                 var diffY = Math.abs(this.startY - e.getPageY());
16296                 if (diffX > this.clickPixelThresh ||
16297                             diffY > this.clickPixelThresh) {
16298                     this.startDrag(this.startX, this.startY);
16299                 }
16300             }
16301
16302             if (this.dragThreshMet) {
16303                 this.dragCurrent.b4Drag(e);
16304                 this.dragCurrent.onDrag(e);
16305                 if(!this.dragCurrent.moveOnly){
16306                     this.fireEvents(e, false);
16307                 }
16308             }
16309
16310             this.stopEvent(e);
16311
16312             return true;
16313         },
16314
16315         /**
16316          * Iterates over all of the DragDrop elements to find ones we are
16317          * hovering over or dropping on
16318          * @method fireEvents
16319          * @param {Event} e the event
16320          * @param {boolean} isDrop is this a drop op or a mouseover op?
16321          * @private
16322          * @static
16323          */
16324         fireEvents: function(e, isDrop) {
16325             var dc = this.dragCurrent;
16326
16327             // If the user did the mouse up outside of the window, we could
16328             // get here even though we have ended the drag.
16329             if (!dc || dc.isLocked()) {
16330                 return;
16331             }
16332
16333             var pt = e.getPoint();
16334
16335             // cache the previous dragOver array
16336             var oldOvers = [];
16337
16338             var outEvts   = [];
16339             var overEvts  = [];
16340             var dropEvts  = [];
16341             var enterEvts = [];
16342
16343             // Check to see if the object(s) we were hovering over is no longer
16344             // being hovered over so we can fire the onDragOut event
16345             for (var i in this.dragOvers) {
16346
16347                 var ddo = this.dragOvers[i];
16348
16349                 if (! this.isTypeOfDD(ddo)) {
16350                     continue;
16351                 }
16352
16353                 if (! this.isOverTarget(pt, ddo, this.mode)) {
16354                     outEvts.push( ddo );
16355                 }
16356
16357                 oldOvers[i] = true;
16358                 delete this.dragOvers[i];
16359             }
16360
16361             for (var sGroup in dc.groups) {
16362
16363                 if ("string" != typeof sGroup) {
16364                     continue;
16365                 }
16366
16367                 for (i in this.ids[sGroup]) {
16368                     var oDD = this.ids[sGroup][i];
16369                     if (! this.isTypeOfDD(oDD)) {
16370                         continue;
16371                     }
16372
16373                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
16374                         if (this.isOverTarget(pt, oDD, this.mode)) {
16375                             // look for drop interactions
16376                             if (isDrop) {
16377                                 dropEvts.push( oDD );
16378                             // look for drag enter and drag over interactions
16379                             } else {
16380
16381                                 // initial drag over: dragEnter fires
16382                                 if (!oldOvers[oDD.id]) {
16383                                     enterEvts.push( oDD );
16384                                 // subsequent drag overs: dragOver fires
16385                                 } else {
16386                                     overEvts.push( oDD );
16387                                 }
16388
16389                                 this.dragOvers[oDD.id] = oDD;
16390                             }
16391                         }
16392                     }
16393                 }
16394             }
16395
16396             if (this.mode) {
16397                 if (outEvts.length) {
16398                     dc.b4DragOut(e, outEvts);
16399                     dc.onDragOut(e, outEvts);
16400                 }
16401
16402                 if (enterEvts.length) {
16403                     dc.onDragEnter(e, enterEvts);
16404                 }
16405
16406                 if (overEvts.length) {
16407                     dc.b4DragOver(e, overEvts);
16408                     dc.onDragOver(e, overEvts);
16409                 }
16410
16411                 if (dropEvts.length) {
16412                     dc.b4DragDrop(e, dropEvts);
16413                     dc.onDragDrop(e, dropEvts);
16414                 }
16415
16416             } else {
16417                 // fire dragout events
16418                 var len = 0;
16419                 for (i=0, len=outEvts.length; i<len; ++i) {
16420                     dc.b4DragOut(e, outEvts[i].id);
16421                     dc.onDragOut(e, outEvts[i].id);
16422                 }
16423
16424                 // fire enter events
16425                 for (i=0,len=enterEvts.length; i<len; ++i) {
16426                     // dc.b4DragEnter(e, oDD.id);
16427                     dc.onDragEnter(e, enterEvts[i].id);
16428                 }
16429
16430                 // fire over events
16431                 for (i=0,len=overEvts.length; i<len; ++i) {
16432                     dc.b4DragOver(e, overEvts[i].id);
16433                     dc.onDragOver(e, overEvts[i].id);
16434                 }
16435
16436                 // fire drop events
16437                 for (i=0, len=dropEvts.length; i<len; ++i) {
16438                     dc.b4DragDrop(e, dropEvts[i].id);
16439                     dc.onDragDrop(e, dropEvts[i].id);
16440                 }
16441
16442             }
16443
16444             // notify about a drop that did not find a target
16445             if (isDrop && !dropEvts.length) {
16446                 dc.onInvalidDrop(e);
16447             }
16448
16449         },
16450
16451         /**
16452          * Helper function for getting the best match from the list of drag
16453          * and drop objects returned by the drag and drop events when we are
16454          * in INTERSECT mode.  It returns either the first object that the
16455          * cursor is over, or the object that has the greatest overlap with
16456          * the dragged element.
16457          * @method getBestMatch
16458          * @param  {DragDrop[]} dds The array of drag and drop objects
16459          * targeted
16460          * @return {DragDrop}       The best single match
16461          * @static
16462          */
16463         getBestMatch: function(dds) {
16464             var winner = null;
16465             // Return null if the input is not what we expect
16466             //if (!dds || !dds.length || dds.length == 0) {
16467                // winner = null;
16468             // If there is only one item, it wins
16469             //} else if (dds.length == 1) {
16470
16471             var len = dds.length;
16472
16473             if (len == 1) {
16474                 winner = dds[0];
16475             } else {
16476                 // Loop through the targeted items
16477                 for (var i=0; i<len; ++i) {
16478                     var dd = dds[i];
16479                     // If the cursor is over the object, it wins.  If the
16480                     // cursor is over multiple matches, the first one we come
16481                     // to wins.
16482                     if (dd.cursorIsOver) {
16483                         winner = dd;
16484                         break;
16485                     // Otherwise the object with the most overlap wins
16486                     } else {
16487                         if (!winner ||
16488                             winner.overlap.getArea() < dd.overlap.getArea()) {
16489                             winner = dd;
16490                         }
16491                     }
16492                 }
16493             }
16494
16495             return winner;
16496         },
16497
16498         /**
16499          * Refreshes the cache of the top-left and bottom-right points of the
16500          * drag and drop objects in the specified group(s).  This is in the
16501          * format that is stored in the drag and drop instance, so typical
16502          * usage is:
16503          * <code>
16504          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
16505          * </code>
16506          * Alternatively:
16507          * <code>
16508          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
16509          * </code>
16510          * @TODO this really should be an indexed array.  Alternatively this
16511          * method could accept both.
16512          * @method refreshCache
16513          * @param {Object} groups an associative array of groups to refresh
16514          * @static
16515          */
16516         refreshCache: function(groups) {
16517             for (var sGroup in groups) {
16518                 if ("string" != typeof sGroup) {
16519                     continue;
16520                 }
16521                 for (var i in this.ids[sGroup]) {
16522                     var oDD = this.ids[sGroup][i];
16523
16524                     if (this.isTypeOfDD(oDD)) {
16525                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
16526                         var loc = this.getLocation(oDD);
16527                         if (loc) {
16528                             this.locationCache[oDD.id] = loc;
16529                         } else {
16530                             delete this.locationCache[oDD.id];
16531                             // this will unregister the drag and drop object if
16532                             // the element is not in a usable state
16533                             // oDD.unreg();
16534                         }
16535                     }
16536                 }
16537             }
16538         },
16539
16540         /**
16541          * This checks to make sure an element exists and is in the DOM.  The
16542          * main purpose is to handle cases where innerHTML is used to remove
16543          * drag and drop objects from the DOM.  IE provides an 'unspecified
16544          * error' when trying to access the offsetParent of such an element
16545          * @method verifyEl
16546          * @param {HTMLElement} el the element to check
16547          * @return {boolean} true if the element looks usable
16548          * @static
16549          */
16550         verifyEl: function(el) {
16551             if (el) {
16552                 var parent;
16553                 if(Roo.isIE){
16554                     try{
16555                         parent = el.offsetParent;
16556                     }catch(e){}
16557                 }else{
16558                     parent = el.offsetParent;
16559                 }
16560                 if (parent) {
16561                     return true;
16562                 }
16563             }
16564
16565             return false;
16566         },
16567
16568         /**
16569          * Returns a Region object containing the drag and drop element's position
16570          * and size, including the padding configured for it
16571          * @method getLocation
16572          * @param {DragDrop} oDD the drag and drop object to get the
16573          *                       location for
16574          * @return {Roo.lib.Region} a Region object representing the total area
16575          *                             the element occupies, including any padding
16576          *                             the instance is configured for.
16577          * @static
16578          */
16579         getLocation: function(oDD) {
16580             if (! this.isTypeOfDD(oDD)) {
16581                 return null;
16582             }
16583
16584             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
16585
16586             try {
16587                 pos= Roo.lib.Dom.getXY(el);
16588             } catch (e) { }
16589
16590             if (!pos) {
16591                 return null;
16592             }
16593
16594             x1 = pos[0];
16595             x2 = x1 + el.offsetWidth;
16596             y1 = pos[1];
16597             y2 = y1 + el.offsetHeight;
16598
16599             t = y1 - oDD.padding[0];
16600             r = x2 + oDD.padding[1];
16601             b = y2 + oDD.padding[2];
16602             l = x1 - oDD.padding[3];
16603
16604             return new Roo.lib.Region( t, r, b, l );
16605         },
16606
16607         /**
16608          * Checks the cursor location to see if it over the target
16609          * @method isOverTarget
16610          * @param {Roo.lib.Point} pt The point to evaluate
16611          * @param {DragDrop} oTarget the DragDrop object we are inspecting
16612          * @return {boolean} true if the mouse is over the target
16613          * @private
16614          * @static
16615          */
16616         isOverTarget: function(pt, oTarget, intersect) {
16617             // use cache if available
16618             var loc = this.locationCache[oTarget.id];
16619             if (!loc || !this.useCache) {
16620                 loc = this.getLocation(oTarget);
16621                 this.locationCache[oTarget.id] = loc;
16622
16623             }
16624
16625             if (!loc) {
16626                 return false;
16627             }
16628
16629             oTarget.cursorIsOver = loc.contains( pt );
16630
16631             // DragDrop is using this as a sanity check for the initial mousedown
16632             // in this case we are done.  In POINT mode, if the drag obj has no
16633             // contraints, we are also done. Otherwise we need to evaluate the
16634             // location of the target as related to the actual location of the
16635             // dragged element.
16636             var dc = this.dragCurrent;
16637             if (!dc || !dc.getTargetCoord ||
16638                     (!intersect && !dc.constrainX && !dc.constrainY)) {
16639                 return oTarget.cursorIsOver;
16640             }
16641
16642             oTarget.overlap = null;
16643
16644             // Get the current location of the drag element, this is the
16645             // location of the mouse event less the delta that represents
16646             // where the original mousedown happened on the element.  We
16647             // need to consider constraints and ticks as well.
16648             var pos = dc.getTargetCoord(pt.x, pt.y);
16649
16650             var el = dc.getDragEl();
16651             var curRegion = new Roo.lib.Region( pos.y,
16652                                                    pos.x + el.offsetWidth,
16653                                                    pos.y + el.offsetHeight,
16654                                                    pos.x );
16655
16656             var overlap = curRegion.intersect(loc);
16657
16658             if (overlap) {
16659                 oTarget.overlap = overlap;
16660                 return (intersect) ? true : oTarget.cursorIsOver;
16661             } else {
16662                 return false;
16663             }
16664         },
16665
16666         /**
16667          * unload event handler
16668          * @method _onUnload
16669          * @private
16670          * @static
16671          */
16672         _onUnload: function(e, me) {
16673             Roo.dd.DragDropMgr.unregAll();
16674         },
16675
16676         /**
16677          * Cleans up the drag and drop events and objects.
16678          * @method unregAll
16679          * @private
16680          * @static
16681          */
16682         unregAll: function() {
16683
16684             if (this.dragCurrent) {
16685                 this.stopDrag();
16686                 this.dragCurrent = null;
16687             }
16688
16689             this._execOnAll("unreg", []);
16690
16691             for (i in this.elementCache) {
16692                 delete this.elementCache[i];
16693             }
16694
16695             this.elementCache = {};
16696             this.ids = {};
16697         },
16698
16699         /**
16700          * A cache of DOM elements
16701          * @property elementCache
16702          * @private
16703          * @static
16704          */
16705         elementCache: {},
16706
16707         /**
16708          * Get the wrapper for the DOM element specified
16709          * @method getElWrapper
16710          * @param {String} id the id of the element to get
16711          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
16712          * @private
16713          * @deprecated This wrapper isn't that useful
16714          * @static
16715          */
16716         getElWrapper: function(id) {
16717             var oWrapper = this.elementCache[id];
16718             if (!oWrapper || !oWrapper.el) {
16719                 oWrapper = this.elementCache[id] =
16720                     new this.ElementWrapper(Roo.getDom(id));
16721             }
16722             return oWrapper;
16723         },
16724
16725         /**
16726          * Returns the actual DOM element
16727          * @method getElement
16728          * @param {String} id the id of the elment to get
16729          * @return {Object} The element
16730          * @deprecated use Roo.getDom instead
16731          * @static
16732          */
16733         getElement: function(id) {
16734             return Roo.getDom(id);
16735         },
16736
16737         /**
16738          * Returns the style property for the DOM element (i.e.,
16739          * document.getElById(id).style)
16740          * @method getCss
16741          * @param {String} id the id of the elment to get
16742          * @return {Object} The style property of the element
16743          * @deprecated use Roo.getDom instead
16744          * @static
16745          */
16746         getCss: function(id) {
16747             var el = Roo.getDom(id);
16748             return (el) ? el.style : null;
16749         },
16750
16751         /**
16752          * Inner class for cached elements
16753          * @class DragDropMgr.ElementWrapper
16754          * @for DragDropMgr
16755          * @private
16756          * @deprecated
16757          */
16758         ElementWrapper: function(el) {
16759                 /**
16760                  * The element
16761                  * @property el
16762                  */
16763                 this.el = el || null;
16764                 /**
16765                  * The element id
16766                  * @property id
16767                  */
16768                 this.id = this.el && el.id;
16769                 /**
16770                  * A reference to the style property
16771                  * @property css
16772                  */
16773                 this.css = this.el && el.style;
16774             },
16775
16776         /**
16777          * Returns the X position of an html element
16778          * @method getPosX
16779          * @param el the element for which to get the position
16780          * @return {int} the X coordinate
16781          * @for DragDropMgr
16782          * @deprecated use Roo.lib.Dom.getX instead
16783          * @static
16784          */
16785         getPosX: function(el) {
16786             return Roo.lib.Dom.getX(el);
16787         },
16788
16789         /**
16790          * Returns the Y position of an html element
16791          * @method getPosY
16792          * @param el the element for which to get the position
16793          * @return {int} the Y coordinate
16794          * @deprecated use Roo.lib.Dom.getY instead
16795          * @static
16796          */
16797         getPosY: function(el) {
16798             return Roo.lib.Dom.getY(el);
16799         },
16800
16801         /**
16802          * Swap two nodes.  In IE, we use the native method, for others we
16803          * emulate the IE behavior
16804          * @method swapNode
16805          * @param n1 the first node to swap
16806          * @param n2 the other node to swap
16807          * @static
16808          */
16809         swapNode: function(n1, n2) {
16810             if (n1.swapNode) {
16811                 n1.swapNode(n2);
16812             } else {
16813                 var p = n2.parentNode;
16814                 var s = n2.nextSibling;
16815
16816                 if (s == n1) {
16817                     p.insertBefore(n1, n2);
16818                 } else if (n2 == n1.nextSibling) {
16819                     p.insertBefore(n2, n1);
16820                 } else {
16821                     n1.parentNode.replaceChild(n2, n1);
16822                     p.insertBefore(n1, s);
16823                 }
16824             }
16825         },
16826
16827         /**
16828          * Returns the current scroll position
16829          * @method getScroll
16830          * @private
16831          * @static
16832          */
16833         getScroll: function () {
16834             var t, l, dde=document.documentElement, db=document.body;
16835             if (dde && (dde.scrollTop || dde.scrollLeft)) {
16836                 t = dde.scrollTop;
16837                 l = dde.scrollLeft;
16838             } else if (db) {
16839                 t = db.scrollTop;
16840                 l = db.scrollLeft;
16841             } else {
16842
16843             }
16844             return { top: t, left: l };
16845         },
16846
16847         /**
16848          * Returns the specified element style property
16849          * @method getStyle
16850          * @param {HTMLElement} el          the element
16851          * @param {string}      styleProp   the style property
16852          * @return {string} The value of the style property
16853          * @deprecated use Roo.lib.Dom.getStyle
16854          * @static
16855          */
16856         getStyle: function(el, styleProp) {
16857             return Roo.fly(el).getStyle(styleProp);
16858         },
16859
16860         /**
16861          * Gets the scrollTop
16862          * @method getScrollTop
16863          * @return {int} the document's scrollTop
16864          * @static
16865          */
16866         getScrollTop: function () { return this.getScroll().top; },
16867
16868         /**
16869          * Gets the scrollLeft
16870          * @method getScrollLeft
16871          * @return {int} the document's scrollTop
16872          * @static
16873          */
16874         getScrollLeft: function () { return this.getScroll().left; },
16875
16876         /**
16877          * Sets the x/y position of an element to the location of the
16878          * target element.
16879          * @method moveToEl
16880          * @param {HTMLElement} moveEl      The element to move
16881          * @param {HTMLElement} targetEl    The position reference element
16882          * @static
16883          */
16884         moveToEl: function (moveEl, targetEl) {
16885             var aCoord = Roo.lib.Dom.getXY(targetEl);
16886             Roo.lib.Dom.setXY(moveEl, aCoord);
16887         },
16888
16889         /**
16890          * Numeric array sort function
16891          * @method numericSort
16892          * @static
16893          */
16894         numericSort: function(a, b) { return (a - b); },
16895
16896         /**
16897          * Internal counter
16898          * @property _timeoutCount
16899          * @private
16900          * @static
16901          */
16902         _timeoutCount: 0,
16903
16904         /**
16905          * Trying to make the load order less important.  Without this we get
16906          * an error if this file is loaded before the Event Utility.
16907          * @method _addListeners
16908          * @private
16909          * @static
16910          */
16911         _addListeners: function() {
16912             var DDM = Roo.dd.DDM;
16913             if ( Roo.lib.Event && document ) {
16914                 DDM._onLoad();
16915             } else {
16916                 if (DDM._timeoutCount > 2000) {
16917                 } else {
16918                     setTimeout(DDM._addListeners, 10);
16919                     if (document && document.body) {
16920                         DDM._timeoutCount += 1;
16921                     }
16922                 }
16923             }
16924         },
16925
16926         /**
16927          * Recursively searches the immediate parent and all child nodes for
16928          * the handle element in order to determine wheter or not it was
16929          * clicked.
16930          * @method handleWasClicked
16931          * @param node the html element to inspect
16932          * @static
16933          */
16934         handleWasClicked: function(node, id) {
16935             if (this.isHandle(id, node.id)) {
16936                 return true;
16937             } else {
16938                 // check to see if this is a text node child of the one we want
16939                 var p = node.parentNode;
16940
16941                 while (p) {
16942                     if (this.isHandle(id, p.id)) {
16943                         return true;
16944                     } else {
16945                         p = p.parentNode;
16946                     }
16947                 }
16948             }
16949
16950             return false;
16951         }
16952
16953     };
16954
16955 }();
16956
16957 // shorter alias, save a few bytes
16958 Roo.dd.DDM = Roo.dd.DragDropMgr;
16959 Roo.dd.DDM._addListeners();
16960
16961 }/*
16962  * Based on:
16963  * Ext JS Library 1.1.1
16964  * Copyright(c) 2006-2007, Ext JS, LLC.
16965  *
16966  * Originally Released Under LGPL - original licence link has changed is not relivant.
16967  *
16968  * Fork - LGPL
16969  * <script type="text/javascript">
16970  */
16971
16972 /**
16973  * @class Roo.dd.DD
16974  * A DragDrop implementation where the linked element follows the
16975  * mouse cursor during a drag.
16976  * @extends Roo.dd.DragDrop
16977  * @constructor
16978  * @param {String} id the id of the linked element
16979  * @param {String} sGroup the group of related DragDrop items
16980  * @param {object} config an object containing configurable attributes
16981  *                Valid properties for DD:
16982  *                    scroll
16983  */
16984 Roo.dd.DD = function(id, sGroup, config) {
16985     if (id) {
16986         this.init(id, sGroup, config);
16987     }
16988 };
16989
16990 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
16991
16992     /**
16993      * When set to true, the utility automatically tries to scroll the browser
16994      * window wehn a drag and drop element is dragged near the viewport boundary.
16995      * Defaults to true.
16996      * @property scroll
16997      * @type boolean
16998      */
16999     scroll: true,
17000
17001     /**
17002      * Sets the pointer offset to the distance between the linked element's top
17003      * left corner and the location the element was clicked
17004      * @method autoOffset
17005      * @param {int} iPageX the X coordinate of the click
17006      * @param {int} iPageY the Y coordinate of the click
17007      */
17008     autoOffset: function(iPageX, iPageY) {
17009         var x = iPageX - this.startPageX;
17010         var y = iPageY - this.startPageY;
17011         this.setDelta(x, y);
17012     },
17013
17014     /**
17015      * Sets the pointer offset.  You can call this directly to force the
17016      * offset to be in a particular location (e.g., pass in 0,0 to set it
17017      * to the center of the object)
17018      * @method setDelta
17019      * @param {int} iDeltaX the distance from the left
17020      * @param {int} iDeltaY the distance from the top
17021      */
17022     setDelta: function(iDeltaX, iDeltaY) {
17023         this.deltaX = iDeltaX;
17024         this.deltaY = iDeltaY;
17025     },
17026
17027     /**
17028      * Sets the drag element to the location of the mousedown or click event,
17029      * maintaining the cursor location relative to the location on the element
17030      * that was clicked.  Override this if you want to place the element in a
17031      * location other than where the cursor is.
17032      * @method setDragElPos
17033      * @param {int} iPageX the X coordinate of the mousedown or drag event
17034      * @param {int} iPageY the Y coordinate of the mousedown or drag event
17035      */
17036     setDragElPos: function(iPageX, iPageY) {
17037         // the first time we do this, we are going to check to make sure
17038         // the element has css positioning
17039
17040         var el = this.getDragEl();
17041         this.alignElWithMouse(el, iPageX, iPageY);
17042     },
17043
17044     /**
17045      * Sets the element to the location of the mousedown or click event,
17046      * maintaining the cursor location relative to the location on the element
17047      * that was clicked.  Override this if you want to place the element in a
17048      * location other than where the cursor is.
17049      * @method alignElWithMouse
17050      * @param {HTMLElement} el the element to move
17051      * @param {int} iPageX the X coordinate of the mousedown or drag event
17052      * @param {int} iPageY the Y coordinate of the mousedown or drag event
17053      */
17054     alignElWithMouse: function(el, iPageX, iPageY) {
17055         var oCoord = this.getTargetCoord(iPageX, iPageY);
17056         var fly = el.dom ? el : Roo.fly(el);
17057         if (!this.deltaSetXY) {
17058             var aCoord = [oCoord.x, oCoord.y];
17059             fly.setXY(aCoord);
17060             var newLeft = fly.getLeft(true);
17061             var newTop  = fly.getTop(true);
17062             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
17063         } else {
17064             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
17065         }
17066
17067         this.cachePosition(oCoord.x, oCoord.y);
17068         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
17069         return oCoord;
17070     },
17071
17072     /**
17073      * Saves the most recent position so that we can reset the constraints and
17074      * tick marks on-demand.  We need to know this so that we can calculate the
17075      * number of pixels the element is offset from its original position.
17076      * @method cachePosition
17077      * @param iPageX the current x position (optional, this just makes it so we
17078      * don't have to look it up again)
17079      * @param iPageY the current y position (optional, this just makes it so we
17080      * don't have to look it up again)
17081      */
17082     cachePosition: function(iPageX, iPageY) {
17083         if (iPageX) {
17084             this.lastPageX = iPageX;
17085             this.lastPageY = iPageY;
17086         } else {
17087             var aCoord = Roo.lib.Dom.getXY(this.getEl());
17088             this.lastPageX = aCoord[0];
17089             this.lastPageY = aCoord[1];
17090         }
17091     },
17092
17093     /**
17094      * Auto-scroll the window if the dragged object has been moved beyond the
17095      * visible window boundary.
17096      * @method autoScroll
17097      * @param {int} x the drag element's x position
17098      * @param {int} y the drag element's y position
17099      * @param {int} h the height of the drag element
17100      * @param {int} w the width of the drag element
17101      * @private
17102      */
17103     autoScroll: function(x, y, h, w) {
17104
17105         if (this.scroll) {
17106             // The client height
17107             var clientH = Roo.lib.Dom.getViewWidth();
17108
17109             // The client width
17110             var clientW = Roo.lib.Dom.getViewHeight();
17111
17112             // The amt scrolled down
17113             var st = this.DDM.getScrollTop();
17114
17115             // The amt scrolled right
17116             var sl = this.DDM.getScrollLeft();
17117
17118             // Location of the bottom of the element
17119             var bot = h + y;
17120
17121             // Location of the right of the element
17122             var right = w + x;
17123
17124             // The distance from the cursor to the bottom of the visible area,
17125             // adjusted so that we don't scroll if the cursor is beyond the
17126             // element drag constraints
17127             var toBot = (clientH + st - y - this.deltaY);
17128
17129             // The distance from the cursor to the right of the visible area
17130             var toRight = (clientW + sl - x - this.deltaX);
17131
17132
17133             // How close to the edge the cursor must be before we scroll
17134             // var thresh = (document.all) ? 100 : 40;
17135             var thresh = 40;
17136
17137             // How many pixels to scroll per autoscroll op.  This helps to reduce
17138             // clunky scrolling. IE is more sensitive about this ... it needs this
17139             // value to be higher.
17140             var scrAmt = (document.all) ? 80 : 30;
17141
17142             // Scroll down if we are near the bottom of the visible page and the
17143             // obj extends below the crease
17144             if ( bot > clientH && toBot < thresh ) {
17145                 window.scrollTo(sl, st + scrAmt);
17146             }
17147
17148             // Scroll up if the window is scrolled down and the top of the object
17149             // goes above the top border
17150             if ( y < st && st > 0 && y - st < thresh ) {
17151                 window.scrollTo(sl, st - scrAmt);
17152             }
17153
17154             // Scroll right if the obj is beyond the right border and the cursor is
17155             // near the border.
17156             if ( right > clientW && toRight < thresh ) {
17157                 window.scrollTo(sl + scrAmt, st);
17158             }
17159
17160             // Scroll left if the window has been scrolled to the right and the obj
17161             // extends past the left border
17162             if ( x < sl && sl > 0 && x - sl < thresh ) {
17163                 window.scrollTo(sl - scrAmt, st);
17164             }
17165         }
17166     },
17167
17168     /**
17169      * Finds the location the element should be placed if we want to move
17170      * it to where the mouse location less the click offset would place us.
17171      * @method getTargetCoord
17172      * @param {int} iPageX the X coordinate of the click
17173      * @param {int} iPageY the Y coordinate of the click
17174      * @return an object that contains the coordinates (Object.x and Object.y)
17175      * @private
17176      */
17177     getTargetCoord: function(iPageX, iPageY) {
17178
17179
17180         var x = iPageX - this.deltaX;
17181         var y = iPageY - this.deltaY;
17182
17183         if (this.constrainX) {
17184             if (x < this.minX) { x = this.minX; }
17185             if (x > this.maxX) { x = this.maxX; }
17186         }
17187
17188         if (this.constrainY) {
17189             if (y < this.minY) { y = this.minY; }
17190             if (y > this.maxY) { y = this.maxY; }
17191         }
17192
17193         x = this.getTick(x, this.xTicks);
17194         y = this.getTick(y, this.yTicks);
17195
17196
17197         return {x:x, y:y};
17198     },
17199
17200     /*
17201      * Sets up config options specific to this class. Overrides
17202      * Roo.dd.DragDrop, but all versions of this method through the
17203      * inheritance chain are called
17204      */
17205     applyConfig: function() {
17206         Roo.dd.DD.superclass.applyConfig.call(this);
17207         this.scroll = (this.config.scroll !== false);
17208     },
17209
17210     /*
17211      * Event that fires prior to the onMouseDown event.  Overrides
17212      * Roo.dd.DragDrop.
17213      */
17214     b4MouseDown: function(e) {
17215         // this.resetConstraints();
17216         this.autoOffset(e.getPageX(),
17217                             e.getPageY());
17218     },
17219
17220     /*
17221      * Event that fires prior to the onDrag event.  Overrides
17222      * Roo.dd.DragDrop.
17223      */
17224     b4Drag: function(e) {
17225         this.setDragElPos(e.getPageX(),
17226                             e.getPageY());
17227     },
17228
17229     toString: function() {
17230         return ("DD " + this.id);
17231     }
17232
17233     //////////////////////////////////////////////////////////////////////////
17234     // Debugging ygDragDrop events that can be overridden
17235     //////////////////////////////////////////////////////////////////////////
17236     /*
17237     startDrag: function(x, y) {
17238     },
17239
17240     onDrag: function(e) {
17241     },
17242
17243     onDragEnter: function(e, id) {
17244     },
17245
17246     onDragOver: function(e, id) {
17247     },
17248
17249     onDragOut: function(e, id) {
17250     },
17251
17252     onDragDrop: function(e, id) {
17253     },
17254
17255     endDrag: function(e) {
17256     }
17257
17258     */
17259
17260 });/*
17261  * Based on:
17262  * Ext JS Library 1.1.1
17263  * Copyright(c) 2006-2007, Ext JS, LLC.
17264  *
17265  * Originally Released Under LGPL - original licence link has changed is not relivant.
17266  *
17267  * Fork - LGPL
17268  * <script type="text/javascript">
17269  */
17270
17271 /**
17272  * @class Roo.dd.DDProxy
17273  * A DragDrop implementation that inserts an empty, bordered div into
17274  * the document that follows the cursor during drag operations.  At the time of
17275  * the click, the frame div is resized to the dimensions of the linked html
17276  * element, and moved to the exact location of the linked element.
17277  *
17278  * References to the "frame" element refer to the single proxy element that
17279  * was created to be dragged in place of all DDProxy elements on the
17280  * page.
17281  *
17282  * @extends Roo.dd.DD
17283  * @constructor
17284  * @param {String} id the id of the linked html element
17285  * @param {String} sGroup the group of related DragDrop objects
17286  * @param {object} config an object containing configurable attributes
17287  *                Valid properties for DDProxy in addition to those in DragDrop:
17288  *                   resizeFrame, centerFrame, dragElId
17289  */
17290 Roo.dd.DDProxy = function(id, sGroup, config) {
17291     if (id) {
17292         this.init(id, sGroup, config);
17293         this.initFrame();
17294     }
17295 };
17296
17297 /**
17298  * The default drag frame div id
17299  * @property Roo.dd.DDProxy.dragElId
17300  * @type String
17301  * @static
17302  */
17303 Roo.dd.DDProxy.dragElId = "ygddfdiv";
17304
17305 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
17306
17307     /**
17308      * By default we resize the drag frame to be the same size as the element
17309      * we want to drag (this is to get the frame effect).  We can turn it off
17310      * if we want a different behavior.
17311      * @property resizeFrame
17312      * @type boolean
17313      */
17314     resizeFrame: true,
17315
17316     /**
17317      * By default the frame is positioned exactly where the drag element is, so
17318      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
17319      * you do not have constraints on the obj is to have the drag frame centered
17320      * around the cursor.  Set centerFrame to true for this effect.
17321      * @property centerFrame
17322      * @type boolean
17323      */
17324     centerFrame: false,
17325
17326     /**
17327      * Creates the proxy element if it does not yet exist
17328      * @method createFrame
17329      */
17330     createFrame: function() {
17331         var self = this;
17332         var body = document.body;
17333
17334         if (!body || !body.firstChild) {
17335             setTimeout( function() { self.createFrame(); }, 50 );
17336             return;
17337         }
17338
17339         var div = this.getDragEl();
17340
17341         if (!div) {
17342             div    = document.createElement("div");
17343             div.id = this.dragElId;
17344             var s  = div.style;
17345
17346             s.position   = "absolute";
17347             s.visibility = "hidden";
17348             s.cursor     = "move";
17349             s.border     = "2px solid #aaa";
17350             s.zIndex     = 999;
17351
17352             // appendChild can blow up IE if invoked prior to the window load event
17353             // while rendering a table.  It is possible there are other scenarios
17354             // that would cause this to happen as well.
17355             body.insertBefore(div, body.firstChild);
17356         }
17357     },
17358
17359     /**
17360      * Initialization for the drag frame element.  Must be called in the
17361      * constructor of all subclasses
17362      * @method initFrame
17363      */
17364     initFrame: function() {
17365         this.createFrame();
17366     },
17367
17368     applyConfig: function() {
17369         Roo.dd.DDProxy.superclass.applyConfig.call(this);
17370
17371         this.resizeFrame = (this.config.resizeFrame !== false);
17372         this.centerFrame = (this.config.centerFrame);
17373         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
17374     },
17375
17376     /**
17377      * Resizes the drag frame to the dimensions of the clicked object, positions
17378      * it over the object, and finally displays it
17379      * @method showFrame
17380      * @param {int} iPageX X click position
17381      * @param {int} iPageY Y click position
17382      * @private
17383      */
17384     showFrame: function(iPageX, iPageY) {
17385         var el = this.getEl();
17386         var dragEl = this.getDragEl();
17387         var s = dragEl.style;
17388
17389         this._resizeProxy();
17390
17391         if (this.centerFrame) {
17392             this.setDelta( Math.round(parseInt(s.width,  10)/2),
17393                            Math.round(parseInt(s.height, 10)/2) );
17394         }
17395
17396         this.setDragElPos(iPageX, iPageY);
17397
17398         Roo.fly(dragEl).show();
17399     },
17400
17401     /**
17402      * The proxy is automatically resized to the dimensions of the linked
17403      * element when a drag is initiated, unless resizeFrame is set to false
17404      * @method _resizeProxy
17405      * @private
17406      */
17407     _resizeProxy: function() {
17408         if (this.resizeFrame) {
17409             var el = this.getEl();
17410             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
17411         }
17412     },
17413
17414     // overrides Roo.dd.DragDrop
17415     b4MouseDown: function(e) {
17416         var x = e.getPageX();
17417         var y = e.getPageY();
17418         this.autoOffset(x, y);
17419         this.setDragElPos(x, y);
17420     },
17421
17422     // overrides Roo.dd.DragDrop
17423     b4StartDrag: function(x, y) {
17424         // show the drag frame
17425         this.showFrame(x, y);
17426     },
17427
17428     // overrides Roo.dd.DragDrop
17429     b4EndDrag: function(e) {
17430         Roo.fly(this.getDragEl()).hide();
17431     },
17432
17433     // overrides Roo.dd.DragDrop
17434     // By default we try to move the element to the last location of the frame.
17435     // This is so that the default behavior mirrors that of Roo.dd.DD.
17436     endDrag: function(e) {
17437
17438         var lel = this.getEl();
17439         var del = this.getDragEl();
17440
17441         // Show the drag frame briefly so we can get its position
17442         del.style.visibility = "";
17443
17444         this.beforeMove();
17445         // Hide the linked element before the move to get around a Safari
17446         // rendering bug.
17447         lel.style.visibility = "hidden";
17448         Roo.dd.DDM.moveToEl(lel, del);
17449         del.style.visibility = "hidden";
17450         lel.style.visibility = "";
17451
17452         this.afterDrag();
17453     },
17454
17455     beforeMove : function(){
17456
17457     },
17458
17459     afterDrag : function(){
17460
17461     },
17462
17463     toString: function() {
17464         return ("DDProxy " + this.id);
17465     }
17466
17467 });
17468 /*
17469  * Based on:
17470  * Ext JS Library 1.1.1
17471  * Copyright(c) 2006-2007, Ext JS, LLC.
17472  *
17473  * Originally Released Under LGPL - original licence link has changed is not relivant.
17474  *
17475  * Fork - LGPL
17476  * <script type="text/javascript">
17477  */
17478
17479  /**
17480  * @class Roo.dd.DDTarget
17481  * A DragDrop implementation that does not move, but can be a drop
17482  * target.  You would get the same result by simply omitting implementation
17483  * for the event callbacks, but this way we reduce the processing cost of the
17484  * event listener and the callbacks.
17485  * @extends Roo.dd.DragDrop
17486  * @constructor
17487  * @param {String} id the id of the element that is a drop target
17488  * @param {String} sGroup the group of related DragDrop objects
17489  * @param {object} config an object containing configurable attributes
17490  *                 Valid properties for DDTarget in addition to those in
17491  *                 DragDrop:
17492  *                    none
17493  */
17494 Roo.dd.DDTarget = function(id, sGroup, config) {
17495     if (id) {
17496         this.initTarget(id, sGroup, config);
17497     }
17498     if (config.listeners || config.events) { 
17499        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
17500             listeners : config.listeners || {}, 
17501             events : config.events || {} 
17502         });    
17503     }
17504 };
17505
17506 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
17507 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
17508     toString: function() {
17509         return ("DDTarget " + this.id);
17510     }
17511 });
17512 /*
17513  * Based on:
17514  * Ext JS Library 1.1.1
17515  * Copyright(c) 2006-2007, Ext JS, LLC.
17516  *
17517  * Originally Released Under LGPL - original licence link has changed is not relivant.
17518  *
17519  * Fork - LGPL
17520  * <script type="text/javascript">
17521  */
17522  
17523
17524 /**
17525  * @class Roo.dd.ScrollManager
17526  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
17527  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
17528  * @singleton
17529  */
17530 Roo.dd.ScrollManager = function(){
17531     var ddm = Roo.dd.DragDropMgr;
17532     var els = {};
17533     var dragEl = null;
17534     var proc = {};
17535     
17536     var onStop = function(e){
17537         dragEl = null;
17538         clearProc();
17539     };
17540     
17541     var triggerRefresh = function(){
17542         if(ddm.dragCurrent){
17543              ddm.refreshCache(ddm.dragCurrent.groups);
17544         }
17545     };
17546     
17547     var doScroll = function(){
17548         if(ddm.dragCurrent){
17549             var dds = Roo.dd.ScrollManager;
17550             if(!dds.animate){
17551                 if(proc.el.scroll(proc.dir, dds.increment)){
17552                     triggerRefresh();
17553                 }
17554             }else{
17555                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
17556             }
17557         }
17558     };
17559     
17560     var clearProc = function(){
17561         if(proc.id){
17562             clearInterval(proc.id);
17563         }
17564         proc.id = 0;
17565         proc.el = null;
17566         proc.dir = "";
17567     };
17568     
17569     var startProc = function(el, dir){
17570         clearProc();
17571         proc.el = el;
17572         proc.dir = dir;
17573         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
17574     };
17575     
17576     var onFire = function(e, isDrop){
17577         if(isDrop || !ddm.dragCurrent){ return; }
17578         var dds = Roo.dd.ScrollManager;
17579         if(!dragEl || dragEl != ddm.dragCurrent){
17580             dragEl = ddm.dragCurrent;
17581             // refresh regions on drag start
17582             dds.refreshCache();
17583         }
17584         
17585         var xy = Roo.lib.Event.getXY(e);
17586         var pt = new Roo.lib.Point(xy[0], xy[1]);
17587         for(var id in els){
17588             var el = els[id], r = el._region;
17589             if(r && r.contains(pt) && el.isScrollable()){
17590                 if(r.bottom - pt.y <= dds.thresh){
17591                     if(proc.el != el){
17592                         startProc(el, "down");
17593                     }
17594                     return;
17595                 }else if(r.right - pt.x <= dds.thresh){
17596                     if(proc.el != el){
17597                         startProc(el, "left");
17598                     }
17599                     return;
17600                 }else if(pt.y - r.top <= dds.thresh){
17601                     if(proc.el != el){
17602                         startProc(el, "up");
17603                     }
17604                     return;
17605                 }else if(pt.x - r.left <= dds.thresh){
17606                     if(proc.el != el){
17607                         startProc(el, "right");
17608                     }
17609                     return;
17610                 }
17611             }
17612         }
17613         clearProc();
17614     };
17615     
17616     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
17617     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
17618     
17619     return {
17620         /**
17621          * Registers new overflow element(s) to auto scroll
17622          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
17623          */
17624         register : function(el){
17625             if(el instanceof Array){
17626                 for(var i = 0, len = el.length; i < len; i++) {
17627                         this.register(el[i]);
17628                 }
17629             }else{
17630                 el = Roo.get(el);
17631                 els[el.id] = el;
17632             }
17633         },
17634         
17635         /**
17636          * Unregisters overflow element(s) so they are no longer scrolled
17637          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
17638          */
17639         unregister : function(el){
17640             if(el instanceof Array){
17641                 for(var i = 0, len = el.length; i < len; i++) {
17642                         this.unregister(el[i]);
17643                 }
17644             }else{
17645                 el = Roo.get(el);
17646                 delete els[el.id];
17647             }
17648         },
17649         
17650         /**
17651          * The number of pixels from the edge of a container the pointer needs to be to 
17652          * trigger scrolling (defaults to 25)
17653          * @type Number
17654          */
17655         thresh : 25,
17656         
17657         /**
17658          * The number of pixels to scroll in each scroll increment (defaults to 50)
17659          * @type Number
17660          */
17661         increment : 100,
17662         
17663         /**
17664          * The frequency of scrolls in milliseconds (defaults to 500)
17665          * @type Number
17666          */
17667         frequency : 500,
17668         
17669         /**
17670          * True to animate the scroll (defaults to true)
17671          * @type Boolean
17672          */
17673         animate: true,
17674         
17675         /**
17676          * The animation duration in seconds - 
17677          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
17678          * @type Number
17679          */
17680         animDuration: .4,
17681         
17682         /**
17683          * Manually trigger a cache refresh.
17684          */
17685         refreshCache : function(){
17686             for(var id in els){
17687                 if(typeof els[id] == 'object'){ // for people extending the object prototype
17688                     els[id]._region = els[id].getRegion();
17689                 }
17690             }
17691         }
17692     };
17693 }();/*
17694  * Based on:
17695  * Ext JS Library 1.1.1
17696  * Copyright(c) 2006-2007, Ext JS, LLC.
17697  *
17698  * Originally Released Under LGPL - original licence link has changed is not relivant.
17699  *
17700  * Fork - LGPL
17701  * <script type="text/javascript">
17702  */
17703  
17704
17705 /**
17706  * @class Roo.dd.Registry
17707  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
17708  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
17709  * @singleton
17710  */
17711 Roo.dd.Registry = function(){
17712     var elements = {}; 
17713     var handles = {}; 
17714     var autoIdSeed = 0;
17715
17716     var getId = function(el, autogen){
17717         if(typeof el == "string"){
17718             return el;
17719         }
17720         var id = el.id;
17721         if(!id && autogen !== false){
17722             id = "roodd-" + (++autoIdSeed);
17723             el.id = id;
17724         }
17725         return id;
17726     };
17727     
17728     return {
17729     /**
17730      * Register a drag drop element
17731      * @param {String|HTMLElement} element The id or DOM node to register
17732      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
17733      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
17734      * knows how to interpret, plus there are some specific properties known to the Registry that should be
17735      * populated in the data object (if applicable):
17736      * <pre>
17737 Value      Description<br />
17738 ---------  ------------------------------------------<br />
17739 handles    Array of DOM nodes that trigger dragging<br />
17740            for the element being registered<br />
17741 isHandle   True if the element passed in triggers<br />
17742            dragging itself, else false
17743 </pre>
17744      */
17745         register : function(el, data){
17746             data = data || {};
17747             if(typeof el == "string"){
17748                 el = document.getElementById(el);
17749             }
17750             data.ddel = el;
17751             elements[getId(el)] = data;
17752             if(data.isHandle !== false){
17753                 handles[data.ddel.id] = data;
17754             }
17755             if(data.handles){
17756                 var hs = data.handles;
17757                 for(var i = 0, len = hs.length; i < len; i++){
17758                         handles[getId(hs[i])] = data;
17759                 }
17760             }
17761         },
17762
17763     /**
17764      * Unregister a drag drop element
17765      * @param {String|HTMLElement}  element The id or DOM node to unregister
17766      */
17767         unregister : function(el){
17768             var id = getId(el, false);
17769             var data = elements[id];
17770             if(data){
17771                 delete elements[id];
17772                 if(data.handles){
17773                     var hs = data.handles;
17774                     for(var i = 0, len = hs.length; i < len; i++){
17775                         delete handles[getId(hs[i], false)];
17776                     }
17777                 }
17778             }
17779         },
17780
17781     /**
17782      * Returns the handle registered for a DOM Node by id
17783      * @param {String|HTMLElement} id The DOM node or id to look up
17784      * @return {Object} handle The custom handle data
17785      */
17786         getHandle : function(id){
17787             if(typeof id != "string"){ // must be element?
17788                 id = id.id;
17789             }
17790             return handles[id];
17791         },
17792
17793     /**
17794      * Returns the handle that is registered for the DOM node that is the target of the event
17795      * @param {Event} e The event
17796      * @return {Object} handle The custom handle data
17797      */
17798         getHandleFromEvent : function(e){
17799             var t = Roo.lib.Event.getTarget(e);
17800             return t ? handles[t.id] : null;
17801         },
17802
17803     /**
17804      * Returns a custom data object that is registered for a DOM node by id
17805      * @param {String|HTMLElement} id The DOM node or id to look up
17806      * @return {Object} data The custom data
17807      */
17808         getTarget : function(id){
17809             if(typeof id != "string"){ // must be element?
17810                 id = id.id;
17811             }
17812             return elements[id];
17813         },
17814
17815     /**
17816      * Returns a custom data object that is registered for the DOM node that is the target of the event
17817      * @param {Event} e The event
17818      * @return {Object} data The custom data
17819      */
17820         getTargetFromEvent : function(e){
17821             var t = Roo.lib.Event.getTarget(e);
17822             return t ? elements[t.id] || handles[t.id] : null;
17823         }
17824     };
17825 }();/*
17826  * Based on:
17827  * Ext JS Library 1.1.1
17828  * Copyright(c) 2006-2007, Ext JS, LLC.
17829  *
17830  * Originally Released Under LGPL - original licence link has changed is not relivant.
17831  *
17832  * Fork - LGPL
17833  * <script type="text/javascript">
17834  */
17835  
17836
17837 /**
17838  * @class Roo.dd.StatusProxy
17839  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
17840  * default drag proxy used by all Roo.dd components.
17841  * @constructor
17842  * @param {Object} config
17843  */
17844 Roo.dd.StatusProxy = function(config){
17845     Roo.apply(this, config);
17846     this.id = this.id || Roo.id();
17847     this.el = new Roo.Layer({
17848         dh: {
17849             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
17850                 {tag: "div", cls: "x-dd-drop-icon"},
17851                 {tag: "div", cls: "x-dd-drag-ghost"}
17852             ]
17853         }, 
17854         shadow: !config || config.shadow !== false
17855     });
17856     this.ghost = Roo.get(this.el.dom.childNodes[1]);
17857     this.dropStatus = this.dropNotAllowed;
17858 };
17859
17860 Roo.dd.StatusProxy.prototype = {
17861     /**
17862      * @cfg {String} dropAllowed
17863      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
17864      */
17865     dropAllowed : "x-dd-drop-ok",
17866     /**
17867      * @cfg {String} dropNotAllowed
17868      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
17869      */
17870     dropNotAllowed : "x-dd-drop-nodrop",
17871
17872     /**
17873      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
17874      * over the current target element.
17875      * @param {String} cssClass The css class for the new drop status indicator image
17876      */
17877     setStatus : function(cssClass){
17878         cssClass = cssClass || this.dropNotAllowed;
17879         if(this.dropStatus != cssClass){
17880             this.el.replaceClass(this.dropStatus, cssClass);
17881             this.dropStatus = cssClass;
17882         }
17883     },
17884
17885     /**
17886      * Resets the status indicator to the default dropNotAllowed value
17887      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
17888      */
17889     reset : function(clearGhost){
17890         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
17891         this.dropStatus = this.dropNotAllowed;
17892         if(clearGhost){
17893             this.ghost.update("");
17894         }
17895     },
17896
17897     /**
17898      * Updates the contents of the ghost element
17899      * @param {String} html The html that will replace the current innerHTML of the ghost element
17900      */
17901     update : function(html){
17902         if(typeof html == "string"){
17903             this.ghost.update(html);
17904         }else{
17905             this.ghost.update("");
17906             html.style.margin = "0";
17907             this.ghost.dom.appendChild(html);
17908         }
17909         // ensure float = none set?? cant remember why though.
17910         var el = this.ghost.dom.firstChild;
17911                 if(el){
17912                         Roo.fly(el).setStyle('float', 'none');
17913                 }
17914     },
17915     
17916     /**
17917      * Returns the underlying proxy {@link Roo.Layer}
17918      * @return {Roo.Layer} el
17919     */
17920     getEl : function(){
17921         return this.el;
17922     },
17923
17924     /**
17925      * Returns the ghost element
17926      * @return {Roo.Element} el
17927      */
17928     getGhost : function(){
17929         return this.ghost;
17930     },
17931
17932     /**
17933      * Hides the proxy
17934      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
17935      */
17936     hide : function(clear){
17937         this.el.hide();
17938         if(clear){
17939             this.reset(true);
17940         }
17941     },
17942
17943     /**
17944      * Stops the repair animation if it's currently running
17945      */
17946     stop : function(){
17947         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
17948             this.anim.stop();
17949         }
17950     },
17951
17952     /**
17953      * Displays this proxy
17954      */
17955     show : function(){
17956         this.el.show();
17957     },
17958
17959     /**
17960      * Force the Layer to sync its shadow and shim positions to the element
17961      */
17962     sync : function(){
17963         this.el.sync();
17964     },
17965
17966     /**
17967      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
17968      * invalid drop operation by the item being dragged.
17969      * @param {Array} xy The XY position of the element ([x, y])
17970      * @param {Function} callback The function to call after the repair is complete
17971      * @param {Object} scope The scope in which to execute the callback
17972      */
17973     repair : function(xy, callback, scope){
17974         this.callback = callback;
17975         this.scope = scope;
17976         if(xy && this.animRepair !== false){
17977             this.el.addClass("x-dd-drag-repair");
17978             this.el.hideUnders(true);
17979             this.anim = this.el.shift({
17980                 duration: this.repairDuration || .5,
17981                 easing: 'easeOut',
17982                 xy: xy,
17983                 stopFx: true,
17984                 callback: this.afterRepair,
17985                 scope: this
17986             });
17987         }else{
17988             this.afterRepair();
17989         }
17990     },
17991
17992     // private
17993     afterRepair : function(){
17994         this.hide(true);
17995         if(typeof this.callback == "function"){
17996             this.callback.call(this.scope || this);
17997         }
17998         this.callback = null;
17999         this.scope = null;
18000     }
18001 };/*
18002  * Based on:
18003  * Ext JS Library 1.1.1
18004  * Copyright(c) 2006-2007, Ext JS, LLC.
18005  *
18006  * Originally Released Under LGPL - original licence link has changed is not relivant.
18007  *
18008  * Fork - LGPL
18009  * <script type="text/javascript">
18010  */
18011
18012 /**
18013  * @class Roo.dd.DragSource
18014  * @extends Roo.dd.DDProxy
18015  * A simple class that provides the basic implementation needed to make any element draggable.
18016  * @constructor
18017  * @param {String/HTMLElement/Element} el The container element
18018  * @param {Object} config
18019  */
18020 Roo.dd.DragSource = function(el, config){
18021     this.el = Roo.get(el);
18022     this.dragData = {};
18023     
18024     Roo.apply(this, config);
18025     
18026     if(!this.proxy){
18027         this.proxy = new Roo.dd.StatusProxy();
18028     }
18029
18030     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
18031           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
18032     
18033     this.dragging = false;
18034 };
18035
18036 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
18037     /**
18038      * @cfg {String} dropAllowed
18039      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
18040      */
18041     dropAllowed : "x-dd-drop-ok",
18042     /**
18043      * @cfg {String} dropNotAllowed
18044      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
18045      */
18046     dropNotAllowed : "x-dd-drop-nodrop",
18047
18048     /**
18049      * Returns the data object associated with this drag source
18050      * @return {Object} data An object containing arbitrary data
18051      */
18052     getDragData : function(e){
18053         return this.dragData;
18054     },
18055
18056     // private
18057     onDragEnter : function(e, id){
18058         var target = Roo.dd.DragDropMgr.getDDById(id);
18059         this.cachedTarget = target;
18060         if(this.beforeDragEnter(target, e, id) !== false){
18061             if(target.isNotifyTarget){
18062                 var status = target.notifyEnter(this, e, this.dragData);
18063                 this.proxy.setStatus(status);
18064             }else{
18065                 this.proxy.setStatus(this.dropAllowed);
18066             }
18067             
18068             if(this.afterDragEnter){
18069                 /**
18070                  * An empty function by default, but provided so that you can perform a custom action
18071                  * when the dragged item enters the drop target by providing an implementation.
18072                  * @param {Roo.dd.DragDrop} target The drop target
18073                  * @param {Event} e The event object
18074                  * @param {String} id The id of the dragged element
18075                  * @method afterDragEnter
18076                  */
18077                 this.afterDragEnter(target, e, id);
18078             }
18079         }
18080     },
18081
18082     /**
18083      * An empty function by default, but provided so that you can perform a custom action
18084      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
18085      * @param {Roo.dd.DragDrop} target The drop target
18086      * @param {Event} e The event object
18087      * @param {String} id The id of the dragged element
18088      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18089      */
18090     beforeDragEnter : function(target, e, id){
18091         return true;
18092     },
18093
18094     // private
18095     alignElWithMouse: function() {
18096         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
18097         this.proxy.sync();
18098     },
18099
18100     // private
18101     onDragOver : function(e, id){
18102         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18103         if(this.beforeDragOver(target, e, id) !== false){
18104             if(target.isNotifyTarget){
18105                 var status = target.notifyOver(this, e, this.dragData);
18106                 this.proxy.setStatus(status);
18107             }
18108
18109             if(this.afterDragOver){
18110                 /**
18111                  * An empty function by default, but provided so that you can perform a custom action
18112                  * while the dragged item is over the drop target by providing an implementation.
18113                  * @param {Roo.dd.DragDrop} target The drop target
18114                  * @param {Event} e The event object
18115                  * @param {String} id The id of the dragged element
18116                  * @method afterDragOver
18117                  */
18118                 this.afterDragOver(target, e, id);
18119             }
18120         }
18121     },
18122
18123     /**
18124      * An empty function by default, but provided so that you can perform a custom action
18125      * while the dragged item is over the drop target and optionally cancel the onDragOver.
18126      * @param {Roo.dd.DragDrop} target The drop target
18127      * @param {Event} e The event object
18128      * @param {String} id The id of the dragged element
18129      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18130      */
18131     beforeDragOver : function(target, e, id){
18132         return true;
18133     },
18134
18135     // private
18136     onDragOut : function(e, id){
18137         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18138         if(this.beforeDragOut(target, e, id) !== false){
18139             if(target.isNotifyTarget){
18140                 target.notifyOut(this, e, this.dragData);
18141             }
18142             this.proxy.reset();
18143             if(this.afterDragOut){
18144                 /**
18145                  * An empty function by default, but provided so that you can perform a custom action
18146                  * after the dragged item is dragged out of the target without dropping.
18147                  * @param {Roo.dd.DragDrop} target The drop target
18148                  * @param {Event} e The event object
18149                  * @param {String} id The id of the dragged element
18150                  * @method afterDragOut
18151                  */
18152                 this.afterDragOut(target, e, id);
18153             }
18154         }
18155         this.cachedTarget = null;
18156     },
18157
18158     /**
18159      * An empty function by default, but provided so that you can perform a custom action before the dragged
18160      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
18161      * @param {Roo.dd.DragDrop} target The drop target
18162      * @param {Event} e The event object
18163      * @param {String} id The id of the dragged element
18164      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18165      */
18166     beforeDragOut : function(target, e, id){
18167         return true;
18168     },
18169     
18170     // private
18171     onDragDrop : function(e, id){
18172         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18173         if(this.beforeDragDrop(target, e, id) !== false){
18174             if(target.isNotifyTarget){
18175                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
18176                     this.onValidDrop(target, e, id);
18177                 }else{
18178                     this.onInvalidDrop(target, e, id);
18179                 }
18180             }else{
18181                 this.onValidDrop(target, e, id);
18182             }
18183             
18184             if(this.afterDragDrop){
18185                 /**
18186                  * An empty function by default, but provided so that you can perform a custom action
18187                  * after a valid drag drop has occurred by providing an implementation.
18188                  * @param {Roo.dd.DragDrop} target The drop target
18189                  * @param {Event} e The event object
18190                  * @param {String} id The id of the dropped element
18191                  * @method afterDragDrop
18192                  */
18193                 this.afterDragDrop(target, e, id);
18194             }
18195         }
18196         delete this.cachedTarget;
18197     },
18198
18199     /**
18200      * An empty function by default, but provided so that you can perform a custom action before the dragged
18201      * item is dropped onto the target and optionally cancel the onDragDrop.
18202      * @param {Roo.dd.DragDrop} target The drop target
18203      * @param {Event} e The event object
18204      * @param {String} id The id of the dragged element
18205      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
18206      */
18207     beforeDragDrop : function(target, e, id){
18208         return true;
18209     },
18210
18211     // private
18212     onValidDrop : function(target, e, id){
18213         this.hideProxy();
18214         if(this.afterValidDrop){
18215             /**
18216              * An empty function by default, but provided so that you can perform a custom action
18217              * after a valid drop has occurred by providing an implementation.
18218              * @param {Object} target The target DD 
18219              * @param {Event} e The event object
18220              * @param {String} id The id of the dropped element
18221              * @method afterInvalidDrop
18222              */
18223             this.afterValidDrop(target, e, id);
18224         }
18225     },
18226
18227     // private
18228     getRepairXY : function(e, data){
18229         return this.el.getXY();  
18230     },
18231
18232     // private
18233     onInvalidDrop : function(target, e, id){
18234         this.beforeInvalidDrop(target, e, id);
18235         if(this.cachedTarget){
18236             if(this.cachedTarget.isNotifyTarget){
18237                 this.cachedTarget.notifyOut(this, e, this.dragData);
18238             }
18239             this.cacheTarget = null;
18240         }
18241         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
18242
18243         if(this.afterInvalidDrop){
18244             /**
18245              * An empty function by default, but provided so that you can perform a custom action
18246              * after an invalid drop has occurred by providing an implementation.
18247              * @param {Event} e The event object
18248              * @param {String} id The id of the dropped element
18249              * @method afterInvalidDrop
18250              */
18251             this.afterInvalidDrop(e, id);
18252         }
18253     },
18254
18255     // private
18256     afterRepair : function(){
18257         if(Roo.enableFx){
18258             this.el.highlight(this.hlColor || "c3daf9");
18259         }
18260         this.dragging = false;
18261     },
18262
18263     /**
18264      * An empty function by default, but provided so that you can perform a custom action after an invalid
18265      * drop has occurred.
18266      * @param {Roo.dd.DragDrop} target The drop target
18267      * @param {Event} e The event object
18268      * @param {String} id The id of the dragged element
18269      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
18270      */
18271     beforeInvalidDrop : function(target, e, id){
18272         return true;
18273     },
18274
18275     // private
18276     handleMouseDown : function(e){
18277         if(this.dragging) {
18278             return;
18279         }
18280         var data = this.getDragData(e);
18281         if(data && this.onBeforeDrag(data, e) !== false){
18282             this.dragData = data;
18283             this.proxy.stop();
18284             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
18285         } 
18286     },
18287
18288     /**
18289      * An empty function by default, but provided so that you can perform a custom action before the initial
18290      * drag event begins and optionally cancel it.
18291      * @param {Object} data An object containing arbitrary data to be shared with drop targets
18292      * @param {Event} e The event object
18293      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18294      */
18295     onBeforeDrag : function(data, e){
18296         return true;
18297     },
18298
18299     /**
18300      * An empty function by default, but provided so that you can perform a custom action once the initial
18301      * drag event has begun.  The drag cannot be canceled from this function.
18302      * @param {Number} x The x position of the click on the dragged object
18303      * @param {Number} y The y position of the click on the dragged object
18304      */
18305     onStartDrag : Roo.emptyFn,
18306
18307     // private - YUI override
18308     startDrag : function(x, y){
18309         this.proxy.reset();
18310         this.dragging = true;
18311         this.proxy.update("");
18312         this.onInitDrag(x, y);
18313         this.proxy.show();
18314     },
18315
18316     // private
18317     onInitDrag : function(x, y){
18318         var clone = this.el.dom.cloneNode(true);
18319         clone.id = Roo.id(); // prevent duplicate ids
18320         this.proxy.update(clone);
18321         this.onStartDrag(x, y);
18322         return true;
18323     },
18324
18325     /**
18326      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
18327      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
18328      */
18329     getProxy : function(){
18330         return this.proxy;  
18331     },
18332
18333     /**
18334      * Hides the drag source's {@link Roo.dd.StatusProxy}
18335      */
18336     hideProxy : function(){
18337         this.proxy.hide();  
18338         this.proxy.reset(true);
18339         this.dragging = false;
18340     },
18341
18342     // private
18343     triggerCacheRefresh : function(){
18344         Roo.dd.DDM.refreshCache(this.groups);
18345     },
18346
18347     // private - override to prevent hiding
18348     b4EndDrag: function(e) {
18349     },
18350
18351     // private - override to prevent moving
18352     endDrag : function(e){
18353         this.onEndDrag(this.dragData, e);
18354     },
18355
18356     // private
18357     onEndDrag : function(data, e){
18358     },
18359     
18360     // private - pin to cursor
18361     autoOffset : function(x, y) {
18362         this.setDelta(-12, -20);
18363     }    
18364 });/*
18365  * Based on:
18366  * Ext JS Library 1.1.1
18367  * Copyright(c) 2006-2007, Ext JS, LLC.
18368  *
18369  * Originally Released Under LGPL - original licence link has changed is not relivant.
18370  *
18371  * Fork - LGPL
18372  * <script type="text/javascript">
18373  */
18374
18375
18376 /**
18377  * @class Roo.dd.DropTarget
18378  * @extends Roo.dd.DDTarget
18379  * A simple class that provides the basic implementation needed to make any element a drop target that can have
18380  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
18381  * @constructor
18382  * @param {String/HTMLElement/Element} el The container element
18383  * @param {Object} config
18384  */
18385 Roo.dd.DropTarget = function(el, config){
18386     this.el = Roo.get(el);
18387     
18388     var listeners = false; ;
18389     if (config && config.listeners) {
18390         listeners= config.listeners;
18391         delete config.listeners;
18392     }
18393     Roo.apply(this, config);
18394     
18395     if(this.containerScroll){
18396         Roo.dd.ScrollManager.register(this.el);
18397     }
18398     this.addEvents( {
18399          /**
18400          * @scope Roo.dd.DropTarget
18401          */
18402          
18403          /**
18404          * @event enter
18405          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
18406          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
18407          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
18408          * 
18409          * IMPORTANT : it should set this.overClass and this.dropAllowed
18410          * 
18411          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18412          * @param {Event} e The event
18413          * @param {Object} data An object containing arbitrary data supplied by the drag source
18414          */
18415         "enter" : true,
18416         
18417          /**
18418          * @event over
18419          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
18420          * This method will be called on every mouse movement while the drag source is over the drop target.
18421          * This default implementation simply returns the dropAllowed config value.
18422          * 
18423          * IMPORTANT : it should set this.dropAllowed
18424          * 
18425          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18426          * @param {Event} e The event
18427          * @param {Object} data An object containing arbitrary data supplied by the drag source
18428          
18429          */
18430         "over" : true,
18431         /**
18432          * @event out
18433          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
18434          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
18435          * overClass (if any) from the drop element.
18436          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18437          * @param {Event} e The event
18438          * @param {Object} data An object containing arbitrary data supplied by the drag source
18439          */
18440          "out" : true,
18441          
18442         /**
18443          * @event drop
18444          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
18445          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
18446          * implementation that does something to process the drop event and returns true so that the drag source's
18447          * repair action does not run.
18448          * 
18449          * IMPORTANT : it should set this.success
18450          * 
18451          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18452          * @param {Event} e The event
18453          * @param {Object} data An object containing arbitrary data supplied by the drag source
18454         */
18455          "drop" : true
18456     });
18457             
18458      
18459     Roo.dd.DropTarget.superclass.constructor.call(  this, 
18460         this.el.dom, 
18461         this.ddGroup || this.group,
18462         {
18463             isTarget: true,
18464             listeners : listeners || {} 
18465            
18466         
18467         }
18468     );
18469
18470 };
18471
18472 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
18473     /**
18474      * @cfg {String} overClass
18475      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
18476      */
18477      /**
18478      * @cfg {String} ddGroup
18479      * The drag drop group to handle drop events for
18480      */
18481      
18482     /**
18483      * @cfg {String} dropAllowed
18484      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
18485      */
18486     dropAllowed : "x-dd-drop-ok",
18487     /**
18488      * @cfg {String} dropNotAllowed
18489      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
18490      */
18491     dropNotAllowed : "x-dd-drop-nodrop",
18492     /**
18493      * @cfg {boolean} success
18494      * set this after drop listener.. 
18495      */
18496     success : false,
18497     /**
18498      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
18499      * if the drop point is valid for over/enter..
18500      */
18501     valid : false,
18502     // private
18503     isTarget : true,
18504
18505     // private
18506     isNotifyTarget : true,
18507     
18508     /**
18509      * @hide
18510      */
18511     notifyEnter : function(dd, e, data)
18512     {
18513         this.valid = true;
18514         this.fireEvent('enter', dd, e, data);
18515         if(this.overClass){
18516             this.el.addClass(this.overClass);
18517         }
18518         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
18519             this.valid ? this.dropAllowed : this.dropNotAllowed
18520         );
18521     },
18522
18523     /**
18524      * @hide
18525      */
18526     notifyOver : function(dd, e, data)
18527     {
18528         this.valid = true;
18529         this.fireEvent('over', dd, e, data);
18530         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
18531             this.valid ? this.dropAllowed : this.dropNotAllowed
18532         );
18533     },
18534
18535     /**
18536      * @hide
18537      */
18538     notifyOut : function(dd, e, data)
18539     {
18540         this.fireEvent('out', dd, e, data);
18541         if(this.overClass){
18542             this.el.removeClass(this.overClass);
18543         }
18544     },
18545
18546     /**
18547      * @hide
18548      */
18549     notifyDrop : function(dd, e, data)
18550     {
18551         this.success = false;
18552         this.fireEvent('drop', dd, e, data);
18553         return this.success;
18554     }
18555 });/*
18556  * Based on:
18557  * Ext JS Library 1.1.1
18558  * Copyright(c) 2006-2007, Ext JS, LLC.
18559  *
18560  * Originally Released Under LGPL - original licence link has changed is not relivant.
18561  *
18562  * Fork - LGPL
18563  * <script type="text/javascript">
18564  */
18565
18566
18567 /**
18568  * @class Roo.dd.DragZone
18569  * @extends Roo.dd.DragSource
18570  * This class provides a container DD instance that proxies for multiple child node sources.<br />
18571  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
18572  * @constructor
18573  * @param {String/HTMLElement/Element} el The container element
18574  * @param {Object} config
18575  */
18576 Roo.dd.DragZone = function(el, config){
18577     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
18578     if(this.containerScroll){
18579         Roo.dd.ScrollManager.register(this.el);
18580     }
18581 };
18582
18583 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
18584     /**
18585      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
18586      * for auto scrolling during drag operations.
18587      */
18588     /**
18589      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
18590      * method after a failed drop (defaults to "c3daf9" - light blue)
18591      */
18592
18593     /**
18594      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
18595      * for a valid target to drag based on the mouse down. Override this method
18596      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
18597      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
18598      * @param {EventObject} e The mouse down event
18599      * @return {Object} The dragData
18600      */
18601     getDragData : function(e){
18602         return Roo.dd.Registry.getHandleFromEvent(e);
18603     },
18604     
18605     /**
18606      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
18607      * this.dragData.ddel
18608      * @param {Number} x The x position of the click on the dragged object
18609      * @param {Number} y The y position of the click on the dragged object
18610      * @return {Boolean} true to continue the drag, false to cancel
18611      */
18612     onInitDrag : function(x, y){
18613         this.proxy.update(this.dragData.ddel.cloneNode(true));
18614         this.onStartDrag(x, y);
18615         return true;
18616     },
18617     
18618     /**
18619      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
18620      */
18621     afterRepair : function(){
18622         if(Roo.enableFx){
18623             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
18624         }
18625         this.dragging = false;
18626     },
18627
18628     /**
18629      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
18630      * the XY of this.dragData.ddel
18631      * @param {EventObject} e The mouse up event
18632      * @return {Array} The xy location (e.g. [100, 200])
18633      */
18634     getRepairXY : function(e){
18635         return Roo.Element.fly(this.dragData.ddel).getXY();  
18636     }
18637 });/*
18638  * Based on:
18639  * Ext JS Library 1.1.1
18640  * Copyright(c) 2006-2007, Ext JS, LLC.
18641  *
18642  * Originally Released Under LGPL - original licence link has changed is not relivant.
18643  *
18644  * Fork - LGPL
18645  * <script type="text/javascript">
18646  */
18647 /**
18648  * @class Roo.dd.DropZone
18649  * @extends Roo.dd.DropTarget
18650  * This class provides a container DD instance that proxies for multiple child node targets.<br />
18651  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
18652  * @constructor
18653  * @param {String/HTMLElement/Element} el The container element
18654  * @param {Object} config
18655  */
18656 Roo.dd.DropZone = function(el, config){
18657     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
18658 };
18659
18660 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
18661     /**
18662      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
18663      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
18664      * provide your own custom lookup.
18665      * @param {Event} e The event
18666      * @return {Object} data The custom data
18667      */
18668     getTargetFromEvent : function(e){
18669         return Roo.dd.Registry.getTargetFromEvent(e);
18670     },
18671
18672     /**
18673      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
18674      * that it has registered.  This method has no default implementation and should be overridden to provide
18675      * node-specific processing if necessary.
18676      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
18677      * {@link #getTargetFromEvent} for this node)
18678      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18679      * @param {Event} e The event
18680      * @param {Object} data An object containing arbitrary data supplied by the drag source
18681      */
18682     onNodeEnter : function(n, dd, e, data){
18683         
18684     },
18685
18686     /**
18687      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
18688      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
18689      * overridden to provide the proper feedback.
18690      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18691      * {@link #getTargetFromEvent} for this node)
18692      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18693      * @param {Event} e The event
18694      * @param {Object} data An object containing arbitrary data supplied by the drag source
18695      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18696      * underlying {@link Roo.dd.StatusProxy} can be updated
18697      */
18698     onNodeOver : function(n, dd, e, data){
18699         return this.dropAllowed;
18700     },
18701
18702     /**
18703      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
18704      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
18705      * node-specific processing if necessary.
18706      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18707      * {@link #getTargetFromEvent} for this node)
18708      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18709      * @param {Event} e The event
18710      * @param {Object} data An object containing arbitrary data supplied by the drag source
18711      */
18712     onNodeOut : function(n, dd, e, data){
18713         
18714     },
18715
18716     /**
18717      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
18718      * the drop node.  The default implementation returns false, so it should be overridden to provide the
18719      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
18720      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18721      * {@link #getTargetFromEvent} for this node)
18722      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18723      * @param {Event} e The event
18724      * @param {Object} data An object containing arbitrary data supplied by the drag source
18725      * @return {Boolean} True if the drop was valid, else false
18726      */
18727     onNodeDrop : function(n, dd, e, data){
18728         return false;
18729     },
18730
18731     /**
18732      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
18733      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
18734      * it should be overridden to provide the proper feedback if necessary.
18735      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18736      * @param {Event} e The event
18737      * @param {Object} data An object containing arbitrary data supplied by the drag source
18738      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18739      * underlying {@link Roo.dd.StatusProxy} can be updated
18740      */
18741     onContainerOver : function(dd, e, data){
18742         return this.dropNotAllowed;
18743     },
18744
18745     /**
18746      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
18747      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
18748      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
18749      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
18750      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18751      * @param {Event} e The event
18752      * @param {Object} data An object containing arbitrary data supplied by the drag source
18753      * @return {Boolean} True if the drop was valid, else false
18754      */
18755     onContainerDrop : function(dd, e, data){
18756         return false;
18757     },
18758
18759     /**
18760      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
18761      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
18762      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
18763      * you should override this method and provide a custom implementation.
18764      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18765      * @param {Event} e The event
18766      * @param {Object} data An object containing arbitrary data supplied by the drag source
18767      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18768      * underlying {@link Roo.dd.StatusProxy} can be updated
18769      */
18770     notifyEnter : function(dd, e, data){
18771         return this.dropNotAllowed;
18772     },
18773
18774     /**
18775      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
18776      * This method will be called on every mouse movement while the drag source is over the drop zone.
18777      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
18778      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
18779      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
18780      * registered node, it will call {@link #onContainerOver}.
18781      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18782      * @param {Event} e The event
18783      * @param {Object} data An object containing arbitrary data supplied by the drag source
18784      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18785      * underlying {@link Roo.dd.StatusProxy} can be updated
18786      */
18787     notifyOver : function(dd, e, data){
18788         var n = this.getTargetFromEvent(e);
18789         if(!n){ // not over valid drop target
18790             if(this.lastOverNode){
18791                 this.onNodeOut(this.lastOverNode, dd, e, data);
18792                 this.lastOverNode = null;
18793             }
18794             return this.onContainerOver(dd, e, data);
18795         }
18796         if(this.lastOverNode != n){
18797             if(this.lastOverNode){
18798                 this.onNodeOut(this.lastOverNode, dd, e, data);
18799             }
18800             this.onNodeEnter(n, dd, e, data);
18801             this.lastOverNode = n;
18802         }
18803         return this.onNodeOver(n, dd, e, data);
18804     },
18805
18806     /**
18807      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
18808      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
18809      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
18810      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18811      * @param {Event} e The event
18812      * @param {Object} data An object containing arbitrary data supplied by the drag zone
18813      */
18814     notifyOut : function(dd, e, data){
18815         if(this.lastOverNode){
18816             this.onNodeOut(this.lastOverNode, dd, e, data);
18817             this.lastOverNode = null;
18818         }
18819     },
18820
18821     /**
18822      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
18823      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
18824      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
18825      * otherwise it will call {@link #onContainerDrop}.
18826      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18827      * @param {Event} e The event
18828      * @param {Object} data An object containing arbitrary data supplied by the drag source
18829      * @return {Boolean} True if the drop was valid, else false
18830      */
18831     notifyDrop : function(dd, e, data){
18832         if(this.lastOverNode){
18833             this.onNodeOut(this.lastOverNode, dd, e, data);
18834             this.lastOverNode = null;
18835         }
18836         var n = this.getTargetFromEvent(e);
18837         return n ?
18838             this.onNodeDrop(n, dd, e, data) :
18839             this.onContainerDrop(dd, e, data);
18840     },
18841
18842     // private
18843     triggerCacheRefresh : function(){
18844         Roo.dd.DDM.refreshCache(this.groups);
18845     }  
18846 });/*
18847  * Based on:
18848  * Ext JS Library 1.1.1
18849  * Copyright(c) 2006-2007, Ext JS, LLC.
18850  *
18851  * Originally Released Under LGPL - original licence link has changed is not relivant.
18852  *
18853  * Fork - LGPL
18854  * <script type="text/javascript">
18855  */
18856
18857
18858 /**
18859  * @class Roo.data.SortTypes
18860  * @singleton
18861  * Defines the default sorting (casting?) comparison functions used when sorting data.
18862  */
18863 Roo.data.SortTypes = {
18864     /**
18865      * Default sort that does nothing
18866      * @param {Mixed} s The value being converted
18867      * @return {Mixed} The comparison value
18868      */
18869     none : function(s){
18870         return s;
18871     },
18872     
18873     /**
18874      * The regular expression used to strip tags
18875      * @type {RegExp}
18876      * @property
18877      */
18878     stripTagsRE : /<\/?[^>]+>/gi,
18879     
18880     /**
18881      * Strips all HTML tags to sort on text only
18882      * @param {Mixed} s The value being converted
18883      * @return {String} The comparison value
18884      */
18885     asText : function(s){
18886         return String(s).replace(this.stripTagsRE, "");
18887     },
18888     
18889     /**
18890      * Strips all HTML tags to sort on text only - Case insensitive
18891      * @param {Mixed} s The value being converted
18892      * @return {String} The comparison value
18893      */
18894     asUCText : function(s){
18895         return String(s).toUpperCase().replace(this.stripTagsRE, "");
18896     },
18897     
18898     /**
18899      * Case insensitive string
18900      * @param {Mixed} s The value being converted
18901      * @return {String} The comparison value
18902      */
18903     asUCString : function(s) {
18904         return String(s).toUpperCase();
18905     },
18906     
18907     /**
18908      * Date sorting
18909      * @param {Mixed} s The value being converted
18910      * @return {Number} The comparison value
18911      */
18912     asDate : function(s) {
18913         if(!s){
18914             return 0;
18915         }
18916         if(s instanceof Date){
18917             return s.getTime();
18918         }
18919         return Date.parse(String(s));
18920     },
18921     
18922     /**
18923      * Float sorting
18924      * @param {Mixed} s The value being converted
18925      * @return {Float} The comparison value
18926      */
18927     asFloat : function(s) {
18928         var val = parseFloat(String(s).replace(/,/g, ""));
18929         if(isNaN(val)) val = 0;
18930         return val;
18931     },
18932     
18933     /**
18934      * Integer sorting
18935      * @param {Mixed} s The value being converted
18936      * @return {Number} The comparison value
18937      */
18938     asInt : function(s) {
18939         var val = parseInt(String(s).replace(/,/g, ""));
18940         if(isNaN(val)) val = 0;
18941         return val;
18942     }
18943 };/*
18944  * Based on:
18945  * Ext JS Library 1.1.1
18946  * Copyright(c) 2006-2007, Ext JS, LLC.
18947  *
18948  * Originally Released Under LGPL - original licence link has changed is not relivant.
18949  *
18950  * Fork - LGPL
18951  * <script type="text/javascript">
18952  */
18953
18954 /**
18955 * @class Roo.data.Record
18956  * Instances of this class encapsulate both record <em>definition</em> information, and record
18957  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
18958  * to access Records cached in an {@link Roo.data.Store} object.<br>
18959  * <p>
18960  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
18961  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
18962  * objects.<br>
18963  * <p>
18964  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
18965  * @constructor
18966  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
18967  * {@link #create}. The parameters are the same.
18968  * @param {Array} data An associative Array of data values keyed by the field name.
18969  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
18970  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
18971  * not specified an integer id is generated.
18972  */
18973 Roo.data.Record = function(data, id){
18974     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
18975     this.data = data;
18976 };
18977
18978 /**
18979  * Generate a constructor for a specific record layout.
18980  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
18981  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
18982  * Each field definition object may contain the following properties: <ul>
18983  * <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,
18984  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
18985  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
18986  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
18987  * is being used, then this is a string containing the javascript expression to reference the data relative to 
18988  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
18989  * to the data item relative to the record element. If the mapping expression is the same as the field name,
18990  * this may be omitted.</p></li>
18991  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
18992  * <ul><li>auto (Default, implies no conversion)</li>
18993  * <li>string</li>
18994  * <li>int</li>
18995  * <li>float</li>
18996  * <li>boolean</li>
18997  * <li>date</li></ul></p></li>
18998  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
18999  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
19000  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
19001  * by the Reader into an object that will be stored in the Record. It is passed the
19002  * following parameters:<ul>
19003  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
19004  * </ul></p></li>
19005  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
19006  * </ul>
19007  * <br>usage:<br><pre><code>
19008 var TopicRecord = Roo.data.Record.create(
19009     {name: 'title', mapping: 'topic_title'},
19010     {name: 'author', mapping: 'username'},
19011     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
19012     {name: 'lastPost', mapping: 'post_time', type: 'date'},
19013     {name: 'lastPoster', mapping: 'user2'},
19014     {name: 'excerpt', mapping: 'post_text'}
19015 );
19016
19017 var myNewRecord = new TopicRecord({
19018     title: 'Do my job please',
19019     author: 'noobie',
19020     totalPosts: 1,
19021     lastPost: new Date(),
19022     lastPoster: 'Animal',
19023     excerpt: 'No way dude!'
19024 });
19025 myStore.add(myNewRecord);
19026 </code></pre>
19027  * @method create
19028  * @static
19029  */
19030 Roo.data.Record.create = function(o){
19031     var f = function(){
19032         f.superclass.constructor.apply(this, arguments);
19033     };
19034     Roo.extend(f, Roo.data.Record);
19035     var p = f.prototype;
19036     p.fields = new Roo.util.MixedCollection(false, function(field){
19037         return field.name;
19038     });
19039     for(var i = 0, len = o.length; i < len; i++){
19040         p.fields.add(new Roo.data.Field(o[i]));
19041     }
19042     f.getField = function(name){
19043         return p.fields.get(name);  
19044     };
19045     return f;
19046 };
19047
19048 Roo.data.Record.AUTO_ID = 1000;
19049 Roo.data.Record.EDIT = 'edit';
19050 Roo.data.Record.REJECT = 'reject';
19051 Roo.data.Record.COMMIT = 'commit';
19052
19053 Roo.data.Record.prototype = {
19054     /**
19055      * Readonly flag - true if this record has been modified.
19056      * @type Boolean
19057      */
19058     dirty : false,
19059     editing : false,
19060     error: null,
19061     modified: null,
19062
19063     // private
19064     join : function(store){
19065         this.store = store;
19066     },
19067
19068     /**
19069      * Set the named field to the specified value.
19070      * @param {String} name The name of the field to set.
19071      * @param {Object} value The value to set the field to.
19072      */
19073     set : function(name, value){
19074         if(this.data[name] == value){
19075             return;
19076         }
19077         this.dirty = true;
19078         if(!this.modified){
19079             this.modified = {};
19080         }
19081         if(typeof this.modified[name] == 'undefined'){
19082             this.modified[name] = this.data[name];
19083         }
19084         this.data[name] = value;
19085         if(!this.editing && this.store){
19086             this.store.afterEdit(this);
19087         }       
19088     },
19089
19090     /**
19091      * Get the value of the named field.
19092      * @param {String} name The name of the field to get the value of.
19093      * @return {Object} The value of the field.
19094      */
19095     get : function(name){
19096         return this.data[name]; 
19097     },
19098
19099     // private
19100     beginEdit : function(){
19101         this.editing = true;
19102         this.modified = {}; 
19103     },
19104
19105     // private
19106     cancelEdit : function(){
19107         this.editing = false;
19108         delete this.modified;
19109     },
19110
19111     // private
19112     endEdit : function(){
19113         this.editing = false;
19114         if(this.dirty && this.store){
19115             this.store.afterEdit(this);
19116         }
19117     },
19118
19119     /**
19120      * Usually called by the {@link Roo.data.Store} which owns the Record.
19121      * Rejects all changes made to the Record since either creation, or the last commit operation.
19122      * Modified fields are reverted to their original values.
19123      * <p>
19124      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
19125      * of reject operations.
19126      */
19127     reject : function(){
19128         var m = this.modified;
19129         for(var n in m){
19130             if(typeof m[n] != "function"){
19131                 this.data[n] = m[n];
19132             }
19133         }
19134         this.dirty = false;
19135         delete this.modified;
19136         this.editing = false;
19137         if(this.store){
19138             this.store.afterReject(this);
19139         }
19140     },
19141
19142     /**
19143      * Usually called by the {@link Roo.data.Store} which owns the Record.
19144      * Commits all changes made to the Record since either creation, or the last commit operation.
19145      * <p>
19146      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
19147      * of commit operations.
19148      */
19149     commit : function(){
19150         this.dirty = false;
19151         delete this.modified;
19152         this.editing = false;
19153         if(this.store){
19154             this.store.afterCommit(this);
19155         }
19156     },
19157
19158     // private
19159     hasError : function(){
19160         return this.error != null;
19161     },
19162
19163     // private
19164     clearError : function(){
19165         this.error = null;
19166     },
19167
19168     /**
19169      * Creates a copy of this record.
19170      * @param {String} id (optional) A new record id if you don't want to use this record's id
19171      * @return {Record}
19172      */
19173     copy : function(newId) {
19174         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
19175     }
19176 };/*
19177  * Based on:
19178  * Ext JS Library 1.1.1
19179  * Copyright(c) 2006-2007, Ext JS, LLC.
19180  *
19181  * Originally Released Under LGPL - original licence link has changed is not relivant.
19182  *
19183  * Fork - LGPL
19184  * <script type="text/javascript">
19185  */
19186
19187
19188
19189 /**
19190  * @class Roo.data.Store
19191  * @extends Roo.util.Observable
19192  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
19193  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
19194  * <p>
19195  * 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
19196  * has no knowledge of the format of the data returned by the Proxy.<br>
19197  * <p>
19198  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
19199  * instances from the data object. These records are cached and made available through accessor functions.
19200  * @constructor
19201  * Creates a new Store.
19202  * @param {Object} config A config object containing the objects needed for the Store to access data,
19203  * and read the data into Records.
19204  */
19205 Roo.data.Store = function(config){
19206     this.data = new Roo.util.MixedCollection(false);
19207     this.data.getKey = function(o){
19208         return o.id;
19209     };
19210     this.baseParams = {};
19211     // private
19212     this.paramNames = {
19213         "start" : "start",
19214         "limit" : "limit",
19215         "sort" : "sort",
19216         "dir" : "dir",
19217         "multisort" : "_multisort"
19218     };
19219
19220     if(config && config.data){
19221         this.inlineData = config.data;
19222         delete config.data;
19223     }
19224
19225     Roo.apply(this, config);
19226     
19227     if(this.reader){ // reader passed
19228         this.reader = Roo.factory(this.reader, Roo.data);
19229         this.reader.xmodule = this.xmodule || false;
19230         if(!this.recordType){
19231             this.recordType = this.reader.recordType;
19232         }
19233         if(this.reader.onMetaChange){
19234             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
19235         }
19236     }
19237
19238     if(this.recordType){
19239         this.fields = this.recordType.prototype.fields;
19240     }
19241     this.modified = [];
19242
19243     this.addEvents({
19244         /**
19245          * @event datachanged
19246          * Fires when the data cache has changed, and a widget which is using this Store
19247          * as a Record cache should refresh its view.
19248          * @param {Store} this
19249          */
19250         datachanged : true,
19251         /**
19252          * @event metachange
19253          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
19254          * @param {Store} this
19255          * @param {Object} meta The JSON metadata
19256          */
19257         metachange : true,
19258         /**
19259          * @event add
19260          * Fires when Records have been added to the Store
19261          * @param {Store} this
19262          * @param {Roo.data.Record[]} records The array of Records added
19263          * @param {Number} index The index at which the record(s) were added
19264          */
19265         add : true,
19266         /**
19267          * @event remove
19268          * Fires when a Record has been removed from the Store
19269          * @param {Store} this
19270          * @param {Roo.data.Record} record The Record that was removed
19271          * @param {Number} index The index at which the record was removed
19272          */
19273         remove : true,
19274         /**
19275          * @event update
19276          * Fires when a Record has been updated
19277          * @param {Store} this
19278          * @param {Roo.data.Record} record The Record that was updated
19279          * @param {String} operation The update operation being performed.  Value may be one of:
19280          * <pre><code>
19281  Roo.data.Record.EDIT
19282  Roo.data.Record.REJECT
19283  Roo.data.Record.COMMIT
19284          * </code></pre>
19285          */
19286         update : true,
19287         /**
19288          * @event clear
19289          * Fires when the data cache has been cleared.
19290          * @param {Store} this
19291          */
19292         clear : true,
19293         /**
19294          * @event beforeload
19295          * Fires before a request is made for a new data object.  If the beforeload handler returns false
19296          * the load action will be canceled.
19297          * @param {Store} this
19298          * @param {Object} options The loading options that were specified (see {@link #load} for details)
19299          */
19300         beforeload : true,
19301         /**
19302          * @event load
19303          * Fires after a new set of Records has been loaded.
19304          * @param {Store} this
19305          * @param {Roo.data.Record[]} records The Records that were loaded
19306          * @param {Object} options The loading options that were specified (see {@link #load} for details)
19307          */
19308         load : true,
19309         /**
19310          * @event loadexception
19311          * Fires if an exception occurs in the Proxy during loading.
19312          * Called with the signature of the Proxy's "loadexception" event.
19313          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
19314          * 
19315          * @param {Proxy} 
19316          * @param {Object} return from JsonData.reader() - success, totalRecords, records
19317          * @param {Object} load options 
19318          * @param {Object} jsonData from your request (normally this contains the Exception)
19319          */
19320         loadexception : true
19321     });
19322     
19323     if(this.proxy){
19324         this.proxy = Roo.factory(this.proxy, Roo.data);
19325         this.proxy.xmodule = this.xmodule || false;
19326         this.relayEvents(this.proxy,  ["loadexception"]);
19327     }
19328     this.sortToggle = {};
19329     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
19330
19331     Roo.data.Store.superclass.constructor.call(this);
19332
19333     if(this.inlineData){
19334         this.loadData(this.inlineData);
19335         delete this.inlineData;
19336     }
19337 };
19338 Roo.extend(Roo.data.Store, Roo.util.Observable, {
19339      /**
19340     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
19341     * without a remote query - used by combo/forms at present.
19342     */
19343     
19344     /**
19345     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
19346     */
19347     /**
19348     * @cfg {Array} data Inline data to be loaded when the store is initialized.
19349     */
19350     /**
19351     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
19352     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
19353     */
19354     /**
19355     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
19356     * on any HTTP request
19357     */
19358     /**
19359     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
19360     */
19361     /**
19362     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
19363     */
19364     multiSort: false,
19365     /**
19366     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
19367     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
19368     */
19369     remoteSort : false,
19370
19371     /**
19372     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
19373      * loaded or when a record is removed. (defaults to false).
19374     */
19375     pruneModifiedRecords : false,
19376
19377     // private
19378     lastOptions : null,
19379
19380     /**
19381      * Add Records to the Store and fires the add event.
19382      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19383      */
19384     add : function(records){
19385         records = [].concat(records);
19386         for(var i = 0, len = records.length; i < len; i++){
19387             records[i].join(this);
19388         }
19389         var index = this.data.length;
19390         this.data.addAll(records);
19391         this.fireEvent("add", this, records, index);
19392     },
19393
19394     /**
19395      * Remove a Record from the Store and fires the remove event.
19396      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
19397      */
19398     remove : function(record){
19399         var index = this.data.indexOf(record);
19400         this.data.removeAt(index);
19401         if(this.pruneModifiedRecords){
19402             this.modified.remove(record);
19403         }
19404         this.fireEvent("remove", this, record, index);
19405     },
19406
19407     /**
19408      * Remove all Records from the Store and fires the clear event.
19409      */
19410     removeAll : function(){
19411         this.data.clear();
19412         if(this.pruneModifiedRecords){
19413             this.modified = [];
19414         }
19415         this.fireEvent("clear", this);
19416     },
19417
19418     /**
19419      * Inserts Records to the Store at the given index and fires the add event.
19420      * @param {Number} index The start index at which to insert the passed Records.
19421      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19422      */
19423     insert : function(index, records){
19424         records = [].concat(records);
19425         for(var i = 0, len = records.length; i < len; i++){
19426             this.data.insert(index, records[i]);
19427             records[i].join(this);
19428         }
19429         this.fireEvent("add", this, records, index);
19430     },
19431
19432     /**
19433      * Get the index within the cache of the passed Record.
19434      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
19435      * @return {Number} The index of the passed Record. Returns -1 if not found.
19436      */
19437     indexOf : function(record){
19438         return this.data.indexOf(record);
19439     },
19440
19441     /**
19442      * Get the index within the cache of the Record with the passed id.
19443      * @param {String} id The id of the Record to find.
19444      * @return {Number} The index of the Record. Returns -1 if not found.
19445      */
19446     indexOfId : function(id){
19447         return this.data.indexOfKey(id);
19448     },
19449
19450     /**
19451      * Get the Record with the specified id.
19452      * @param {String} id The id of the Record to find.
19453      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
19454      */
19455     getById : function(id){
19456         return this.data.key(id);
19457     },
19458
19459     /**
19460      * Get the Record at the specified index.
19461      * @param {Number} index The index of the Record to find.
19462      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
19463      */
19464     getAt : function(index){
19465         return this.data.itemAt(index);
19466     },
19467
19468     /**
19469      * Returns a range of Records between specified indices.
19470      * @param {Number} startIndex (optional) The starting index (defaults to 0)
19471      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
19472      * @return {Roo.data.Record[]} An array of Records
19473      */
19474     getRange : function(start, end){
19475         return this.data.getRange(start, end);
19476     },
19477
19478     // private
19479     storeOptions : function(o){
19480         o = Roo.apply({}, o);
19481         delete o.callback;
19482         delete o.scope;
19483         this.lastOptions = o;
19484     },
19485
19486     /**
19487      * Loads the Record cache from the configured Proxy using the configured Reader.
19488      * <p>
19489      * If using remote paging, then the first load call must specify the <em>start</em>
19490      * and <em>limit</em> properties in the options.params property to establish the initial
19491      * position within the dataset, and the number of Records to cache on each read from the Proxy.
19492      * <p>
19493      * <strong>It is important to note that for remote data sources, loading is asynchronous,
19494      * and this call will return before the new data has been loaded. Perform any post-processing
19495      * in a callback function, or in a "load" event handler.</strong>
19496      * <p>
19497      * @param {Object} options An object containing properties which control loading options:<ul>
19498      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
19499      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
19500      * passed the following arguments:<ul>
19501      * <li>r : Roo.data.Record[]</li>
19502      * <li>options: Options object from the load call</li>
19503      * <li>success: Boolean success indicator</li></ul></li>
19504      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
19505      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
19506      * </ul>
19507      */
19508     load : function(options){
19509         options = options || {};
19510         if(this.fireEvent("beforeload", this, options) !== false){
19511             this.storeOptions(options);
19512             var p = Roo.apply(options.params || {}, this.baseParams);
19513             // if meta was not loaded from remote source.. try requesting it.
19514             if (!this.reader.metaFromRemote) {
19515                 p._requestMeta = 1;
19516             }
19517             if(this.sortInfo && this.remoteSort){
19518                 var pn = this.paramNames;
19519                 p[pn["sort"]] = this.sortInfo.field;
19520                 p[pn["dir"]] = this.sortInfo.direction;
19521             }
19522             if (this.multiSort) {
19523                 var pn = this.paramNames;
19524                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
19525             }
19526             
19527             this.proxy.load(p, this.reader, this.loadRecords, this, options);
19528         }
19529     },
19530
19531     /**
19532      * Reloads the Record cache from the configured Proxy using the configured Reader and
19533      * the options from the last load operation performed.
19534      * @param {Object} options (optional) An object containing properties which may override the options
19535      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
19536      * the most recently used options are reused).
19537      */
19538     reload : function(options){
19539         this.load(Roo.applyIf(options||{}, this.lastOptions));
19540     },
19541
19542     // private
19543     // Called as a callback by the Reader during a load operation.
19544     loadRecords : function(o, options, success){
19545         if(!o || success === false){
19546             if(success !== false){
19547                 this.fireEvent("load", this, [], options);
19548             }
19549             if(options.callback){
19550                 options.callback.call(options.scope || this, [], options, false);
19551             }
19552             return;
19553         }
19554         // if data returned failure - throw an exception.
19555         if (o.success === false) {
19556             // show a message if no listener is registered.
19557             if (!this.hasListener('loadexception') && typeof(this.reader.jsonData.errorMsg) != 'undefined') {
19558                     Roo.MessageBox.alert("Error loading",this.reader.jsonData.errorMsg);
19559             }
19560             // loadmask wil be hooked into this..
19561             this.fireEvent("loadexception", this, o, options, this.reader.jsonData);
19562             return;
19563         }
19564         var r = o.records, t = o.totalRecords || r.length;
19565         if(!options || options.add !== true){
19566             if(this.pruneModifiedRecords){
19567                 this.modified = [];
19568             }
19569             for(var i = 0, len = r.length; i < len; i++){
19570                 r[i].join(this);
19571             }
19572             if(this.snapshot){
19573                 this.data = this.snapshot;
19574                 delete this.snapshot;
19575             }
19576             this.data.clear();
19577             this.data.addAll(r);
19578             this.totalLength = t;
19579             this.applySort();
19580             this.fireEvent("datachanged", this);
19581         }else{
19582             this.totalLength = Math.max(t, this.data.length+r.length);
19583             this.add(r);
19584         }
19585         this.fireEvent("load", this, r, options);
19586         if(options.callback){
19587             options.callback.call(options.scope || this, r, options, true);
19588         }
19589     },
19590
19591
19592     /**
19593      * Loads data from a passed data block. A Reader which understands the format of the data
19594      * must have been configured in the constructor.
19595      * @param {Object} data The data block from which to read the Records.  The format of the data expected
19596      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
19597      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
19598      */
19599     loadData : function(o, append){
19600         var r = this.reader.readRecords(o);
19601         this.loadRecords(r, {add: append}, true);
19602     },
19603
19604     /**
19605      * Gets the number of cached records.
19606      * <p>
19607      * <em>If using paging, this may not be the total size of the dataset. If the data object
19608      * used by the Reader contains the dataset size, then the getTotalCount() function returns
19609      * the data set size</em>
19610      */
19611     getCount : function(){
19612         return this.data.length || 0;
19613     },
19614
19615     /**
19616      * Gets the total number of records in the dataset as returned by the server.
19617      * <p>
19618      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
19619      * the dataset size</em>
19620      */
19621     getTotalCount : function(){
19622         return this.totalLength || 0;
19623     },
19624
19625     /**
19626      * Returns the sort state of the Store as an object with two properties:
19627      * <pre><code>
19628  field {String} The name of the field by which the Records are sorted
19629  direction {String} The sort order, "ASC" or "DESC"
19630      * </code></pre>
19631      */
19632     getSortState : function(){
19633         return this.sortInfo;
19634     },
19635
19636     // private
19637     applySort : function(){
19638         if(this.sortInfo && !this.remoteSort){
19639             var s = this.sortInfo, f = s.field;
19640             var st = this.fields.get(f).sortType;
19641             var fn = function(r1, r2){
19642                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
19643                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
19644             };
19645             this.data.sort(s.direction, fn);
19646             if(this.snapshot && this.snapshot != this.data){
19647                 this.snapshot.sort(s.direction, fn);
19648             }
19649         }
19650     },
19651
19652     /**
19653      * Sets the default sort column and order to be used by the next load operation.
19654      * @param {String} fieldName The name of the field to sort by.
19655      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
19656      */
19657     setDefaultSort : function(field, dir){
19658         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
19659     },
19660
19661     /**
19662      * Sort the Records.
19663      * If remote sorting is used, the sort is performed on the server, and the cache is
19664      * reloaded. If local sorting is used, the cache is sorted internally.
19665      * @param {String} fieldName The name of the field to sort by.
19666      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
19667      */
19668     sort : function(fieldName, dir){
19669         var f = this.fields.get(fieldName);
19670         if(!dir){
19671             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
19672             
19673             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
19674                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
19675             }else{
19676                 dir = f.sortDir;
19677             }
19678         }
19679         this.sortToggle[f.name] = dir;
19680         this.sortInfo = {field: f.name, direction: dir};
19681         if(!this.remoteSort){
19682             this.applySort();
19683             this.fireEvent("datachanged", this);
19684         }else{
19685             this.load(this.lastOptions);
19686         }
19687     },
19688
19689     /**
19690      * Calls the specified function for each of the Records in the cache.
19691      * @param {Function} fn The function to call. The Record is passed as the first parameter.
19692      * Returning <em>false</em> aborts and exits the iteration.
19693      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
19694      */
19695     each : function(fn, scope){
19696         this.data.each(fn, scope);
19697     },
19698
19699     /**
19700      * Gets all records modified since the last commit.  Modified records are persisted across load operations
19701      * (e.g., during paging).
19702      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
19703      */
19704     getModifiedRecords : function(){
19705         return this.modified;
19706     },
19707
19708     // private
19709     createFilterFn : function(property, value, anyMatch){
19710         if(!value.exec){ // not a regex
19711             value = String(value);
19712             if(value.length == 0){
19713                 return false;
19714             }
19715             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
19716         }
19717         return function(r){
19718             return value.test(r.data[property]);
19719         };
19720     },
19721
19722     /**
19723      * Sums the value of <i>property</i> for each record between start and end and returns the result.
19724      * @param {String} property A field on your records
19725      * @param {Number} start The record index to start at (defaults to 0)
19726      * @param {Number} end The last record index to include (defaults to length - 1)
19727      * @return {Number} The sum
19728      */
19729     sum : function(property, start, end){
19730         var rs = this.data.items, v = 0;
19731         start = start || 0;
19732         end = (end || end === 0) ? end : rs.length-1;
19733
19734         for(var i = start; i <= end; i++){
19735             v += (rs[i].data[property] || 0);
19736         }
19737         return v;
19738     },
19739
19740     /**
19741      * Filter the records by a specified property.
19742      * @param {String} field A field on your records
19743      * @param {String/RegExp} value Either a string that the field
19744      * should start with or a RegExp to test against the field
19745      * @param {Boolean} anyMatch True to match any part not just the beginning
19746      */
19747     filter : function(property, value, anyMatch){
19748         var fn = this.createFilterFn(property, value, anyMatch);
19749         return fn ? this.filterBy(fn) : this.clearFilter();
19750     },
19751
19752     /**
19753      * Filter by a function. The specified function will be called with each
19754      * record in this data source. If the function returns true the record is included,
19755      * otherwise it is filtered.
19756      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
19757      * @param {Object} scope (optional) The scope of the function (defaults to this)
19758      */
19759     filterBy : function(fn, scope){
19760         this.snapshot = this.snapshot || this.data;
19761         this.data = this.queryBy(fn, scope||this);
19762         this.fireEvent("datachanged", this);
19763     },
19764
19765     /**
19766      * Query the records by a specified property.
19767      * @param {String} field A field on your records
19768      * @param {String/RegExp} value Either a string that the field
19769      * should start with or a RegExp to test against the field
19770      * @param {Boolean} anyMatch True to match any part not just the beginning
19771      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
19772      */
19773     query : function(property, value, anyMatch){
19774         var fn = this.createFilterFn(property, value, anyMatch);
19775         return fn ? this.queryBy(fn) : this.data.clone();
19776     },
19777
19778     /**
19779      * Query by a function. The specified function will be called with each
19780      * record in this data source. If the function returns true the record is included
19781      * in the results.
19782      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
19783      * @param {Object} scope (optional) The scope of the function (defaults to this)
19784       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
19785      **/
19786     queryBy : function(fn, scope){
19787         var data = this.snapshot || this.data;
19788         return data.filterBy(fn, scope||this);
19789     },
19790
19791     /**
19792      * Collects unique values for a particular dataIndex from this store.
19793      * @param {String} dataIndex The property to collect
19794      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
19795      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
19796      * @return {Array} An array of the unique values
19797      **/
19798     collect : function(dataIndex, allowNull, bypassFilter){
19799         var d = (bypassFilter === true && this.snapshot) ?
19800                 this.snapshot.items : this.data.items;
19801         var v, sv, r = [], l = {};
19802         for(var i = 0, len = d.length; i < len; i++){
19803             v = d[i].data[dataIndex];
19804             sv = String(v);
19805             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
19806                 l[sv] = true;
19807                 r[r.length] = v;
19808             }
19809         }
19810         return r;
19811     },
19812
19813     /**
19814      * Revert to a view of the Record cache with no filtering applied.
19815      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
19816      */
19817     clearFilter : function(suppressEvent){
19818         if(this.snapshot && this.snapshot != this.data){
19819             this.data = this.snapshot;
19820             delete this.snapshot;
19821             if(suppressEvent !== true){
19822                 this.fireEvent("datachanged", this);
19823             }
19824         }
19825     },
19826
19827     // private
19828     afterEdit : function(record){
19829         if(this.modified.indexOf(record) == -1){
19830             this.modified.push(record);
19831         }
19832         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
19833     },
19834     
19835     // private
19836     afterReject : function(record){
19837         this.modified.remove(record);
19838         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
19839     },
19840
19841     // private
19842     afterCommit : function(record){
19843         this.modified.remove(record);
19844         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
19845     },
19846
19847     /**
19848      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
19849      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
19850      */
19851     commitChanges : function(){
19852         var m = this.modified.slice(0);
19853         this.modified = [];
19854         for(var i = 0, len = m.length; i < len; i++){
19855             m[i].commit();
19856         }
19857     },
19858
19859     /**
19860      * Cancel outstanding changes on all changed records.
19861      */
19862     rejectChanges : function(){
19863         var m = this.modified.slice(0);
19864         this.modified = [];
19865         for(var i = 0, len = m.length; i < len; i++){
19866             m[i].reject();
19867         }
19868     },
19869
19870     onMetaChange : function(meta, rtype, o){
19871         this.recordType = rtype;
19872         this.fields = rtype.prototype.fields;
19873         delete this.snapshot;
19874         this.sortInfo = meta.sortInfo || this.sortInfo;
19875         this.modified = [];
19876         this.fireEvent('metachange', this, this.reader.meta);
19877     }
19878 });/*
19879  * Based on:
19880  * Ext JS Library 1.1.1
19881  * Copyright(c) 2006-2007, Ext JS, LLC.
19882  *
19883  * Originally Released Under LGPL - original licence link has changed is not relivant.
19884  *
19885  * Fork - LGPL
19886  * <script type="text/javascript">
19887  */
19888
19889 /**
19890  * @class Roo.data.SimpleStore
19891  * @extends Roo.data.Store
19892  * Small helper class to make creating Stores from Array data easier.
19893  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
19894  * @cfg {Array} fields An array of field definition objects, or field name strings.
19895  * @cfg {Array} data The multi-dimensional array of data
19896  * @constructor
19897  * @param {Object} config
19898  */
19899 Roo.data.SimpleStore = function(config){
19900     Roo.data.SimpleStore.superclass.constructor.call(this, {
19901         isLocal : true,
19902         reader: new Roo.data.ArrayReader({
19903                 id: config.id
19904             },
19905             Roo.data.Record.create(config.fields)
19906         ),
19907         proxy : new Roo.data.MemoryProxy(config.data)
19908     });
19909     this.load();
19910 };
19911 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
19912  * Based on:
19913  * Ext JS Library 1.1.1
19914  * Copyright(c) 2006-2007, Ext JS, LLC.
19915  *
19916  * Originally Released Under LGPL - original licence link has changed is not relivant.
19917  *
19918  * Fork - LGPL
19919  * <script type="text/javascript">
19920  */
19921
19922 /**
19923 /**
19924  * @extends Roo.data.Store
19925  * @class Roo.data.JsonStore
19926  * Small helper class to make creating Stores for JSON data easier. <br/>
19927 <pre><code>
19928 var store = new Roo.data.JsonStore({
19929     url: 'get-images.php',
19930     root: 'images',
19931     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
19932 });
19933 </code></pre>
19934  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
19935  * JsonReader and HttpProxy (unless inline data is provided).</b>
19936  * @cfg {Array} fields An array of field definition objects, or field name strings.
19937  * @constructor
19938  * @param {Object} config
19939  */
19940 Roo.data.JsonStore = function(c){
19941     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
19942         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
19943         reader: new Roo.data.JsonReader(c, c.fields)
19944     }));
19945 };
19946 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
19947  * Based on:
19948  * Ext JS Library 1.1.1
19949  * Copyright(c) 2006-2007, Ext JS, LLC.
19950  *
19951  * Originally Released Under LGPL - original licence link has changed is not relivant.
19952  *
19953  * Fork - LGPL
19954  * <script type="text/javascript">
19955  */
19956
19957  
19958 Roo.data.Field = function(config){
19959     if(typeof config == "string"){
19960         config = {name: config};
19961     }
19962     Roo.apply(this, config);
19963     
19964     if(!this.type){
19965         this.type = "auto";
19966     }
19967     
19968     var st = Roo.data.SortTypes;
19969     // named sortTypes are supported, here we look them up
19970     if(typeof this.sortType == "string"){
19971         this.sortType = st[this.sortType];
19972     }
19973     
19974     // set default sortType for strings and dates
19975     if(!this.sortType){
19976         switch(this.type){
19977             case "string":
19978                 this.sortType = st.asUCString;
19979                 break;
19980             case "date":
19981                 this.sortType = st.asDate;
19982                 break;
19983             default:
19984                 this.sortType = st.none;
19985         }
19986     }
19987
19988     // define once
19989     var stripRe = /[\$,%]/g;
19990
19991     // prebuilt conversion function for this field, instead of
19992     // switching every time we're reading a value
19993     if(!this.convert){
19994         var cv, dateFormat = this.dateFormat;
19995         switch(this.type){
19996             case "":
19997             case "auto":
19998             case undefined:
19999                 cv = function(v){ return v; };
20000                 break;
20001             case "string":
20002                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
20003                 break;
20004             case "int":
20005                 cv = function(v){
20006                     return v !== undefined && v !== null && v !== '' ?
20007                            parseInt(String(v).replace(stripRe, ""), 10) : '';
20008                     };
20009                 break;
20010             case "float":
20011                 cv = function(v){
20012                     return v !== undefined && v !== null && v !== '' ?
20013                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
20014                     };
20015                 break;
20016             case "bool":
20017             case "boolean":
20018                 cv = function(v){ return v === true || v === "true" || v == 1; };
20019                 break;
20020             case "date":
20021                 cv = function(v){
20022                     if(!v){
20023                         return '';
20024                     }
20025                     if(v instanceof Date){
20026                         return v;
20027                     }
20028                     if(dateFormat){
20029                         if(dateFormat == "timestamp"){
20030                             return new Date(v*1000);
20031                         }
20032                         return Date.parseDate(v, dateFormat);
20033                     }
20034                     var parsed = Date.parse(v);
20035                     return parsed ? new Date(parsed) : null;
20036                 };
20037              break;
20038             
20039         }
20040         this.convert = cv;
20041     }
20042 };
20043
20044 Roo.data.Field.prototype = {
20045     dateFormat: null,
20046     defaultValue: "",
20047     mapping: null,
20048     sortType : null,
20049     sortDir : "ASC"
20050 };/*
20051  * Based on:
20052  * Ext JS Library 1.1.1
20053  * Copyright(c) 2006-2007, Ext JS, LLC.
20054  *
20055  * Originally Released Under LGPL - original licence link has changed is not relivant.
20056  *
20057  * Fork - LGPL
20058  * <script type="text/javascript">
20059  */
20060  
20061 // Base class for reading structured data from a data source.  This class is intended to be
20062 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
20063
20064 /**
20065  * @class Roo.data.DataReader
20066  * Base class for reading structured data from a data source.  This class is intended to be
20067  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
20068  */
20069
20070 Roo.data.DataReader = function(meta, recordType){
20071     
20072     this.meta = meta;
20073     
20074     this.recordType = recordType instanceof Array ? 
20075         Roo.data.Record.create(recordType) : recordType;
20076 };
20077
20078 Roo.data.DataReader.prototype = {
20079      /**
20080      * Create an empty record
20081      * @param {Object} data (optional) - overlay some values
20082      * @return {Roo.data.Record} record created.
20083      */
20084     newRow :  function(d) {
20085         var da =  {};
20086         this.recordType.prototype.fields.each(function(c) {
20087             switch( c.type) {
20088                 case 'int' : da[c.name] = 0; break;
20089                 case 'date' : da[c.name] = new Date(); break;
20090                 case 'float' : da[c.name] = 0.0; break;
20091                 case 'boolean' : da[c.name] = false; break;
20092                 default : da[c.name] = ""; break;
20093             }
20094             
20095         });
20096         return new this.recordType(Roo.apply(da, d));
20097     }
20098     
20099 };/*
20100  * Based on:
20101  * Ext JS Library 1.1.1
20102  * Copyright(c) 2006-2007, Ext JS, LLC.
20103  *
20104  * Originally Released Under LGPL - original licence link has changed is not relivant.
20105  *
20106  * Fork - LGPL
20107  * <script type="text/javascript">
20108  */
20109
20110 /**
20111  * @class Roo.data.DataProxy
20112  * @extends Roo.data.Observable
20113  * This class is an abstract base class for implementations which provide retrieval of
20114  * unformatted data objects.<br>
20115  * <p>
20116  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
20117  * (of the appropriate type which knows how to parse the data object) to provide a block of
20118  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
20119  * <p>
20120  * Custom implementations must implement the load method as described in
20121  * {@link Roo.data.HttpProxy#load}.
20122  */
20123 Roo.data.DataProxy = function(){
20124     this.addEvents({
20125         /**
20126          * @event beforeload
20127          * Fires before a network request is made to retrieve a data object.
20128          * @param {Object} This DataProxy object.
20129          * @param {Object} params The params parameter to the load function.
20130          */
20131         beforeload : true,
20132         /**
20133          * @event load
20134          * Fires before the load method's callback is called.
20135          * @param {Object} This DataProxy object.
20136          * @param {Object} o The data object.
20137          * @param {Object} arg The callback argument object passed to the load function.
20138          */
20139         load : true,
20140         /**
20141          * @event loadexception
20142          * Fires if an Exception occurs during data retrieval.
20143          * @param {Object} This DataProxy object.
20144          * @param {Object} o The data object.
20145          * @param {Object} arg The callback argument object passed to the load function.
20146          * @param {Object} e The Exception.
20147          */
20148         loadexception : true
20149     });
20150     Roo.data.DataProxy.superclass.constructor.call(this);
20151 };
20152
20153 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
20154
20155     /**
20156      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
20157      */
20158 /*
20159  * Based on:
20160  * Ext JS Library 1.1.1
20161  * Copyright(c) 2006-2007, Ext JS, LLC.
20162  *
20163  * Originally Released Under LGPL - original licence link has changed is not relivant.
20164  *
20165  * Fork - LGPL
20166  * <script type="text/javascript">
20167  */
20168 /**
20169  * @class Roo.data.MemoryProxy
20170  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
20171  * to the Reader when its load method is called.
20172  * @constructor
20173  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
20174  */
20175 Roo.data.MemoryProxy = function(data){
20176     if (data.data) {
20177         data = data.data;
20178     }
20179     Roo.data.MemoryProxy.superclass.constructor.call(this);
20180     this.data = data;
20181 };
20182
20183 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
20184     /**
20185      * Load data from the requested source (in this case an in-memory
20186      * data object passed to the constructor), read the data object into
20187      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
20188      * process that block using the passed callback.
20189      * @param {Object} params This parameter is not used by the MemoryProxy class.
20190      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20191      * object into a block of Roo.data.Records.
20192      * @param {Function} callback The function into which to pass the block of Roo.data.records.
20193      * The function must be passed <ul>
20194      * <li>The Record block object</li>
20195      * <li>The "arg" argument from the load function</li>
20196      * <li>A boolean success indicator</li>
20197      * </ul>
20198      * @param {Object} scope The scope in which to call the callback
20199      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20200      */
20201     load : function(params, reader, callback, scope, arg){
20202         params = params || {};
20203         var result;
20204         try {
20205             result = reader.readRecords(this.data);
20206         }catch(e){
20207             this.fireEvent("loadexception", this, arg, null, e);
20208             callback.call(scope, null, arg, false);
20209             return;
20210         }
20211         callback.call(scope, result, arg, true);
20212     },
20213     
20214     // private
20215     update : function(params, records){
20216         
20217     }
20218 });/*
20219  * Based on:
20220  * Ext JS Library 1.1.1
20221  * Copyright(c) 2006-2007, Ext JS, LLC.
20222  *
20223  * Originally Released Under LGPL - original licence link has changed is not relivant.
20224  *
20225  * Fork - LGPL
20226  * <script type="text/javascript">
20227  */
20228 /**
20229  * @class Roo.data.HttpProxy
20230  * @extends Roo.data.DataProxy
20231  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
20232  * configured to reference a certain URL.<br><br>
20233  * <p>
20234  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
20235  * from which the running page was served.<br><br>
20236  * <p>
20237  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
20238  * <p>
20239  * Be aware that to enable the browser to parse an XML document, the server must set
20240  * the Content-Type header in the HTTP response to "text/xml".
20241  * @constructor
20242  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
20243  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
20244  * will be used to make the request.
20245  */
20246 Roo.data.HttpProxy = function(conn){
20247     Roo.data.HttpProxy.superclass.constructor.call(this);
20248     // is conn a conn config or a real conn?
20249     this.conn = conn;
20250     this.useAjax = !conn || !conn.events;
20251   
20252 };
20253
20254 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
20255     // thse are take from connection...
20256     
20257     /**
20258      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
20259      */
20260     /**
20261      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
20262      * extra parameters to each request made by this object. (defaults to undefined)
20263      */
20264     /**
20265      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
20266      *  to each request made by this object. (defaults to undefined)
20267      */
20268     /**
20269      * @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)
20270      */
20271     /**
20272      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
20273      */
20274      /**
20275      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
20276      * @type Boolean
20277      */
20278   
20279
20280     /**
20281      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
20282      * @type Boolean
20283      */
20284     /**
20285      * Return the {@link Roo.data.Connection} object being used by this Proxy.
20286      * @return {Connection} The Connection object. This object may be used to subscribe to events on
20287      * a finer-grained basis than the DataProxy events.
20288      */
20289     getConnection : function(){
20290         return this.useAjax ? Roo.Ajax : this.conn;
20291     },
20292
20293     /**
20294      * Load data from the configured {@link Roo.data.Connection}, read the data object into
20295      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
20296      * process that block using the passed callback.
20297      * @param {Object} params An object containing properties which are to be used as HTTP parameters
20298      * for the request to the remote server.
20299      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20300      * object into a block of Roo.data.Records.
20301      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
20302      * The function must be passed <ul>
20303      * <li>The Record block object</li>
20304      * <li>The "arg" argument from the load function</li>
20305      * <li>A boolean success indicator</li>
20306      * </ul>
20307      * @param {Object} scope The scope in which to call the callback
20308      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20309      */
20310     load : function(params, reader, callback, scope, arg){
20311         if(this.fireEvent("beforeload", this, params) !== false){
20312             var  o = {
20313                 params : params || {},
20314                 request: {
20315                     callback : callback,
20316                     scope : scope,
20317                     arg : arg
20318                 },
20319                 reader: reader,
20320                 callback : this.loadResponse,
20321                 scope: this
20322             };
20323             if(this.useAjax){
20324                 Roo.applyIf(o, this.conn);
20325                 if(this.activeRequest){
20326                     Roo.Ajax.abort(this.activeRequest);
20327                 }
20328                 this.activeRequest = Roo.Ajax.request(o);
20329             }else{
20330                 this.conn.request(o);
20331             }
20332         }else{
20333             callback.call(scope||this, null, arg, false);
20334         }
20335     },
20336
20337     // private
20338     loadResponse : function(o, success, response){
20339         delete this.activeRequest;
20340         if(!success){
20341             this.fireEvent("loadexception", this, o, response);
20342             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20343             return;
20344         }
20345         var result;
20346         try {
20347             result = o.reader.read(response);
20348         }catch(e){
20349             this.fireEvent("loadexception", this, o, response, e);
20350             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20351             return;
20352         }
20353         
20354         this.fireEvent("load", this, o, o.request.arg);
20355         o.request.callback.call(o.request.scope, result, o.request.arg, true);
20356     },
20357
20358     // private
20359     update : function(dataSet){
20360
20361     },
20362
20363     // private
20364     updateResponse : function(dataSet){
20365
20366     }
20367 });/*
20368  * Based on:
20369  * Ext JS Library 1.1.1
20370  * Copyright(c) 2006-2007, Ext JS, LLC.
20371  *
20372  * Originally Released Under LGPL - original licence link has changed is not relivant.
20373  *
20374  * Fork - LGPL
20375  * <script type="text/javascript">
20376  */
20377
20378 /**
20379  * @class Roo.data.ScriptTagProxy
20380  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
20381  * other than the originating domain of the running page.<br><br>
20382  * <p>
20383  * <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
20384  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
20385  * <p>
20386  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
20387  * source code that is used as the source inside a &lt;script> tag.<br><br>
20388  * <p>
20389  * In order for the browser to process the returned data, the server must wrap the data object
20390  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
20391  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
20392  * depending on whether the callback name was passed:
20393  * <p>
20394  * <pre><code>
20395 boolean scriptTag = false;
20396 String cb = request.getParameter("callback");
20397 if (cb != null) {
20398     scriptTag = true;
20399     response.setContentType("text/javascript");
20400 } else {
20401     response.setContentType("application/x-json");
20402 }
20403 Writer out = response.getWriter();
20404 if (scriptTag) {
20405     out.write(cb + "(");
20406 }
20407 out.print(dataBlock.toJsonString());
20408 if (scriptTag) {
20409     out.write(");");
20410 }
20411 </pre></code>
20412  *
20413  * @constructor
20414  * @param {Object} config A configuration object.
20415  */
20416 Roo.data.ScriptTagProxy = function(config){
20417     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
20418     Roo.apply(this, config);
20419     this.head = document.getElementsByTagName("head")[0];
20420 };
20421
20422 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
20423
20424 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
20425     /**
20426      * @cfg {String} url The URL from which to request the data object.
20427      */
20428     /**
20429      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
20430      */
20431     timeout : 30000,
20432     /**
20433      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
20434      * the server the name of the callback function set up by the load call to process the returned data object.
20435      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
20436      * javascript output which calls this named function passing the data object as its only parameter.
20437      */
20438     callbackParam : "callback",
20439     /**
20440      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
20441      * name to the request.
20442      */
20443     nocache : true,
20444
20445     /**
20446      * Load data from the configured URL, read the data object into
20447      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
20448      * process that block using the passed callback.
20449      * @param {Object} params An object containing properties which are to be used as HTTP parameters
20450      * for the request to the remote server.
20451      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20452      * object into a block of Roo.data.Records.
20453      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
20454      * The function must be passed <ul>
20455      * <li>The Record block object</li>
20456      * <li>The "arg" argument from the load function</li>
20457      * <li>A boolean success indicator</li>
20458      * </ul>
20459      * @param {Object} scope The scope in which to call the callback
20460      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20461      */
20462     load : function(params, reader, callback, scope, arg){
20463         if(this.fireEvent("beforeload", this, params) !== false){
20464
20465             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
20466
20467             var url = this.url;
20468             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
20469             if(this.nocache){
20470                 url += "&_dc=" + (new Date().getTime());
20471             }
20472             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
20473             var trans = {
20474                 id : transId,
20475                 cb : "stcCallback"+transId,
20476                 scriptId : "stcScript"+transId,
20477                 params : params,
20478                 arg : arg,
20479                 url : url,
20480                 callback : callback,
20481                 scope : scope,
20482                 reader : reader
20483             };
20484             var conn = this;
20485
20486             window[trans.cb] = function(o){
20487                 conn.handleResponse(o, trans);
20488             };
20489
20490             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
20491
20492             if(this.autoAbort !== false){
20493                 this.abort();
20494             }
20495
20496             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
20497
20498             var script = document.createElement("script");
20499             script.setAttribute("src", url);
20500             script.setAttribute("type", "text/javascript");
20501             script.setAttribute("id", trans.scriptId);
20502             this.head.appendChild(script);
20503
20504             this.trans = trans;
20505         }else{
20506             callback.call(scope||this, null, arg, false);
20507         }
20508     },
20509
20510     // private
20511     isLoading : function(){
20512         return this.trans ? true : false;
20513     },
20514
20515     /**
20516      * Abort the current server request.
20517      */
20518     abort : function(){
20519         if(this.isLoading()){
20520             this.destroyTrans(this.trans);
20521         }
20522     },
20523
20524     // private
20525     destroyTrans : function(trans, isLoaded){
20526         this.head.removeChild(document.getElementById(trans.scriptId));
20527         clearTimeout(trans.timeoutId);
20528         if(isLoaded){
20529             window[trans.cb] = undefined;
20530             try{
20531                 delete window[trans.cb];
20532             }catch(e){}
20533         }else{
20534             // if hasn't been loaded, wait for load to remove it to prevent script error
20535             window[trans.cb] = function(){
20536                 window[trans.cb] = undefined;
20537                 try{
20538                     delete window[trans.cb];
20539                 }catch(e){}
20540             };
20541         }
20542     },
20543
20544     // private
20545     handleResponse : function(o, trans){
20546         this.trans = false;
20547         this.destroyTrans(trans, true);
20548         var result;
20549         try {
20550             result = trans.reader.readRecords(o);
20551         }catch(e){
20552             this.fireEvent("loadexception", this, o, trans.arg, e);
20553             trans.callback.call(trans.scope||window, null, trans.arg, false);
20554             return;
20555         }
20556         this.fireEvent("load", this, o, trans.arg);
20557         trans.callback.call(trans.scope||window, result, trans.arg, true);
20558     },
20559
20560     // private
20561     handleFailure : function(trans){
20562         this.trans = false;
20563         this.destroyTrans(trans, false);
20564         this.fireEvent("loadexception", this, null, trans.arg);
20565         trans.callback.call(trans.scope||window, null, trans.arg, false);
20566     }
20567 });/*
20568  * Based on:
20569  * Ext JS Library 1.1.1
20570  * Copyright(c) 2006-2007, Ext JS, LLC.
20571  *
20572  * Originally Released Under LGPL - original licence link has changed is not relivant.
20573  *
20574  * Fork - LGPL
20575  * <script type="text/javascript">
20576  */
20577
20578 /**
20579  * @class Roo.data.JsonReader
20580  * @extends Roo.data.DataReader
20581  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
20582  * based on mappings in a provided Roo.data.Record constructor.
20583  * 
20584  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
20585  * in the reply previously. 
20586  * 
20587  * <p>
20588  * Example code:
20589  * <pre><code>
20590 var RecordDef = Roo.data.Record.create([
20591     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
20592     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
20593 ]);
20594 var myReader = new Roo.data.JsonReader({
20595     totalProperty: "results",    // The property which contains the total dataset size (optional)
20596     root: "rows",                // The property which contains an Array of row objects
20597     id: "id"                     // The property within each row object that provides an ID for the record (optional)
20598 }, RecordDef);
20599 </code></pre>
20600  * <p>
20601  * This would consume a JSON file like this:
20602  * <pre><code>
20603 { 'results': 2, 'rows': [
20604     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
20605     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
20606 }
20607 </code></pre>
20608  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
20609  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
20610  * paged from the remote server.
20611  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
20612  * @cfg {String} root name of the property which contains the Array of row objects.
20613  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
20614  * @constructor
20615  * Create a new JsonReader
20616  * @param {Object} meta Metadata configuration options
20617  * @param {Object} recordType Either an Array of field definition objects,
20618  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
20619  */
20620 Roo.data.JsonReader = function(meta, recordType){
20621     
20622     meta = meta || {};
20623     // set some defaults:
20624     Roo.applyIf(meta, {
20625         totalProperty: 'total',
20626         successProperty : 'success',
20627         root : 'data',
20628         id : 'id'
20629     });
20630     
20631     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
20632 };
20633 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
20634     
20635     /**
20636      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
20637      * Used by Store query builder to append _requestMeta to params.
20638      * 
20639      */
20640     metaFromRemote : false,
20641     /**
20642      * This method is only used by a DataProxy which has retrieved data from a remote server.
20643      * @param {Object} response The XHR object which contains the JSON data in its responseText.
20644      * @return {Object} data A data block which is used by an Roo.data.Store object as
20645      * a cache of Roo.data.Records.
20646      */
20647     read : function(response){
20648         var json = response.responseText;
20649        
20650         var o = /* eval:var:o */ eval("("+json+")");
20651         if(!o) {
20652             throw {message: "JsonReader.read: Json object not found"};
20653         }
20654         
20655         if(o.metaData){
20656             
20657             delete this.ef;
20658             this.metaFromRemote = true;
20659             this.meta = o.metaData;
20660             this.recordType = Roo.data.Record.create(o.metaData.fields);
20661             this.onMetaChange(this.meta, this.recordType, o);
20662         }
20663         return this.readRecords(o);
20664     },
20665
20666     // private function a store will implement
20667     onMetaChange : function(meta, recordType, o){
20668
20669     },
20670
20671     /**
20672          * @ignore
20673          */
20674     simpleAccess: function(obj, subsc) {
20675         return obj[subsc];
20676     },
20677
20678         /**
20679          * @ignore
20680          */
20681     getJsonAccessor: function(){
20682         var re = /[\[\.]/;
20683         return function(expr) {
20684             try {
20685                 return(re.test(expr))
20686                     ? new Function("obj", "return obj." + expr)
20687                     : function(obj){
20688                         return obj[expr];
20689                     };
20690             } catch(e){}
20691             return Roo.emptyFn;
20692         };
20693     }(),
20694
20695     /**
20696      * Create a data block containing Roo.data.Records from an XML document.
20697      * @param {Object} o An object which contains an Array of row objects in the property specified
20698      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
20699      * which contains the total size of the dataset.
20700      * @return {Object} data A data block which is used by an Roo.data.Store object as
20701      * a cache of Roo.data.Records.
20702      */
20703     readRecords : function(o){
20704         /**
20705          * After any data loads, the raw JSON data is available for further custom processing.
20706          * @type Object
20707          */
20708         this.jsonData = o;
20709         var s = this.meta, Record = this.recordType,
20710             f = Record.prototype.fields, fi = f.items, fl = f.length;
20711
20712 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
20713         if (!this.ef) {
20714             if(s.totalProperty) {
20715                     this.getTotal = this.getJsonAccessor(s.totalProperty);
20716                 }
20717                 if(s.successProperty) {
20718                     this.getSuccess = this.getJsonAccessor(s.successProperty);
20719                 }
20720                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
20721                 if (s.id) {
20722                         var g = this.getJsonAccessor(s.id);
20723                         this.getId = function(rec) {
20724                                 var r = g(rec);
20725                                 return (r === undefined || r === "") ? null : r;
20726                         };
20727                 } else {
20728                         this.getId = function(){return null;};
20729                 }
20730             this.ef = [];
20731             for(var jj = 0; jj < fl; jj++){
20732                 f = fi[jj];
20733                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
20734                 this.ef[jj] = this.getJsonAccessor(map);
20735             }
20736         }
20737
20738         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
20739         if(s.totalProperty){
20740             var vt = parseInt(this.getTotal(o), 10);
20741             if(!isNaN(vt)){
20742                 totalRecords = vt;
20743             }
20744         }
20745         if(s.successProperty){
20746             var vs = this.getSuccess(o);
20747             if(vs === false || vs === 'false'){
20748                 success = false;
20749             }
20750         }
20751         var records = [];
20752             for(var i = 0; i < c; i++){
20753                     var n = root[i];
20754                 var values = {};
20755                 var id = this.getId(n);
20756                 for(var j = 0; j < fl; j++){
20757                     f = fi[j];
20758                 var v = this.ef[j](n);
20759                 if (!f.convert) {
20760                     Roo.log('missing convert for ' + f.name);
20761                     Roo.log(f);
20762                     continue;
20763                 }
20764                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
20765                 }
20766                 var record = new Record(values, id);
20767                 record.json = n;
20768                 records[i] = record;
20769             }
20770             return {
20771                 success : success,
20772                 records : records,
20773                 totalRecords : totalRecords
20774             };
20775     }
20776 });/*
20777  * Based on:
20778  * Ext JS Library 1.1.1
20779  * Copyright(c) 2006-2007, Ext JS, LLC.
20780  *
20781  * Originally Released Under LGPL - original licence link has changed is not relivant.
20782  *
20783  * Fork - LGPL
20784  * <script type="text/javascript">
20785  */
20786
20787 /**
20788  * @class Roo.data.XmlReader
20789  * @extends Roo.data.DataReader
20790  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
20791  * based on mappings in a provided Roo.data.Record constructor.<br><br>
20792  * <p>
20793  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
20794  * header in the HTTP response must be set to "text/xml".</em>
20795  * <p>
20796  * Example code:
20797  * <pre><code>
20798 var RecordDef = Roo.data.Record.create([
20799    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
20800    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
20801 ]);
20802 var myReader = new Roo.data.XmlReader({
20803    totalRecords: "results", // The element which contains the total dataset size (optional)
20804    record: "row",           // The repeated element which contains row information
20805    id: "id"                 // The element within the row that provides an ID for the record (optional)
20806 }, RecordDef);
20807 </code></pre>
20808  * <p>
20809  * This would consume an XML file like this:
20810  * <pre><code>
20811 &lt;?xml?>
20812 &lt;dataset>
20813  &lt;results>2&lt;/results>
20814  &lt;row>
20815    &lt;id>1&lt;/id>
20816    &lt;name>Bill&lt;/name>
20817    &lt;occupation>Gardener&lt;/occupation>
20818  &lt;/row>
20819  &lt;row>
20820    &lt;id>2&lt;/id>
20821    &lt;name>Ben&lt;/name>
20822    &lt;occupation>Horticulturalist&lt;/occupation>
20823  &lt;/row>
20824 &lt;/dataset>
20825 </code></pre>
20826  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
20827  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
20828  * paged from the remote server.
20829  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
20830  * @cfg {String} success The DomQuery path to the success attribute used by forms.
20831  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
20832  * a record identifier value.
20833  * @constructor
20834  * Create a new XmlReader
20835  * @param {Object} meta Metadata configuration options
20836  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
20837  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
20838  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
20839  */
20840 Roo.data.XmlReader = function(meta, recordType){
20841     meta = meta || {};
20842     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
20843 };
20844 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
20845     /**
20846      * This method is only used by a DataProxy which has retrieved data from a remote server.
20847          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
20848          * to contain a method called 'responseXML' that returns an XML document object.
20849      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
20850      * a cache of Roo.data.Records.
20851      */
20852     read : function(response){
20853         var doc = response.responseXML;
20854         if(!doc) {
20855             throw {message: "XmlReader.read: XML Document not available"};
20856         }
20857         return this.readRecords(doc);
20858     },
20859
20860     /**
20861      * Create a data block containing Roo.data.Records from an XML document.
20862          * @param {Object} doc A parsed XML document.
20863      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
20864      * a cache of Roo.data.Records.
20865      */
20866     readRecords : function(doc){
20867         /**
20868          * After any data loads/reads, the raw XML Document is available for further custom processing.
20869          * @type XMLDocument
20870          */
20871         this.xmlData = doc;
20872         var root = doc.documentElement || doc;
20873         var q = Roo.DomQuery;
20874         var recordType = this.recordType, fields = recordType.prototype.fields;
20875         var sid = this.meta.id;
20876         var totalRecords = 0, success = true;
20877         if(this.meta.totalRecords){
20878             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
20879         }
20880         
20881         if(this.meta.success){
20882             var sv = q.selectValue(this.meta.success, root, true);
20883             success = sv !== false && sv !== 'false';
20884         }
20885         var records = [];
20886         var ns = q.select(this.meta.record, root);
20887         for(var i = 0, len = ns.length; i < len; i++) {
20888                 var n = ns[i];
20889                 var values = {};
20890                 var id = sid ? q.selectValue(sid, n) : undefined;
20891                 for(var j = 0, jlen = fields.length; j < jlen; j++){
20892                     var f = fields.items[j];
20893                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
20894                     v = f.convert(v);
20895                     values[f.name] = v;
20896                 }
20897                 var record = new recordType(values, id);
20898                 record.node = n;
20899                 records[records.length] = record;
20900             }
20901
20902             return {
20903                 success : success,
20904                 records : records,
20905                 totalRecords : totalRecords || records.length
20906             };
20907     }
20908 });/*
20909  * Based on:
20910  * Ext JS Library 1.1.1
20911  * Copyright(c) 2006-2007, Ext JS, LLC.
20912  *
20913  * Originally Released Under LGPL - original licence link has changed is not relivant.
20914  *
20915  * Fork - LGPL
20916  * <script type="text/javascript">
20917  */
20918
20919 /**
20920  * @class Roo.data.ArrayReader
20921  * @extends Roo.data.DataReader
20922  * Data reader class to create an Array of Roo.data.Record objects from an Array.
20923  * Each element of that Array represents a row of data fields. The
20924  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
20925  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
20926  * <p>
20927  * Example code:.
20928  * <pre><code>
20929 var RecordDef = Roo.data.Record.create([
20930     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
20931     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
20932 ]);
20933 var myReader = new Roo.data.ArrayReader({
20934     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
20935 }, RecordDef);
20936 </code></pre>
20937  * <p>
20938  * This would consume an Array like this:
20939  * <pre><code>
20940 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
20941   </code></pre>
20942  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
20943  * @constructor
20944  * Create a new JsonReader
20945  * @param {Object} meta Metadata configuration options.
20946  * @param {Object} recordType Either an Array of field definition objects
20947  * as specified to {@link Roo.data.Record#create},
20948  * or an {@link Roo.data.Record} object
20949  * created using {@link Roo.data.Record#create}.
20950  */
20951 Roo.data.ArrayReader = function(meta, recordType){
20952     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
20953 };
20954
20955 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
20956     /**
20957      * Create a data block containing Roo.data.Records from an XML document.
20958      * @param {Object} o An Array of row objects which represents the dataset.
20959      * @return {Object} data A data block which is used by an Roo.data.Store object as
20960      * a cache of Roo.data.Records.
20961      */
20962     readRecords : function(o){
20963         var sid = this.meta ? this.meta.id : null;
20964         var recordType = this.recordType, fields = recordType.prototype.fields;
20965         var records = [];
20966         var root = o;
20967             for(var i = 0; i < root.length; i++){
20968                     var n = root[i];
20969                 var values = {};
20970                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
20971                 for(var j = 0, jlen = fields.length; j < jlen; j++){
20972                 var f = fields.items[j];
20973                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
20974                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
20975                 v = f.convert(v);
20976                 values[f.name] = v;
20977             }
20978                 var record = new recordType(values, id);
20979                 record.json = n;
20980                 records[records.length] = record;
20981             }
20982             return {
20983                 records : records,
20984                 totalRecords : records.length
20985             };
20986     }
20987 });/*
20988  * Based on:
20989  * Ext JS Library 1.1.1
20990  * Copyright(c) 2006-2007, Ext JS, LLC.
20991  *
20992  * Originally Released Under LGPL - original licence link has changed is not relivant.
20993  *
20994  * Fork - LGPL
20995  * <script type="text/javascript">
20996  */
20997
20998
20999 /**
21000  * @class Roo.data.Tree
21001  * @extends Roo.util.Observable
21002  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
21003  * in the tree have most standard DOM functionality.
21004  * @constructor
21005  * @param {Node} root (optional) The root node
21006  */
21007 Roo.data.Tree = function(root){
21008    this.nodeHash = {};
21009    /**
21010     * The root node for this tree
21011     * @type Node
21012     */
21013    this.root = null;
21014    if(root){
21015        this.setRootNode(root);
21016    }
21017    this.addEvents({
21018        /**
21019         * @event append
21020         * Fires when a new child node is appended to a node in this tree.
21021         * @param {Tree} tree The owner tree
21022         * @param {Node} parent The parent node
21023         * @param {Node} node The newly appended node
21024         * @param {Number} index The index of the newly appended node
21025         */
21026        "append" : true,
21027        /**
21028         * @event remove
21029         * Fires when a child node is removed from a node in this tree.
21030         * @param {Tree} tree The owner tree
21031         * @param {Node} parent The parent node
21032         * @param {Node} node The child node removed
21033         */
21034        "remove" : true,
21035        /**
21036         * @event move
21037         * Fires when a node is moved to a new location in the tree
21038         * @param {Tree} tree The owner tree
21039         * @param {Node} node The node moved
21040         * @param {Node} oldParent The old parent of this node
21041         * @param {Node} newParent The new parent of this node
21042         * @param {Number} index The index it was moved to
21043         */
21044        "move" : true,
21045        /**
21046         * @event insert
21047         * Fires when a new child node is inserted in a node in this tree.
21048         * @param {Tree} tree The owner tree
21049         * @param {Node} parent The parent node
21050         * @param {Node} node The child node inserted
21051         * @param {Node} refNode The child node the node was inserted before
21052         */
21053        "insert" : true,
21054        /**
21055         * @event beforeappend
21056         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
21057         * @param {Tree} tree The owner tree
21058         * @param {Node} parent The parent node
21059         * @param {Node} node The child node to be appended
21060         */
21061        "beforeappend" : true,
21062        /**
21063         * @event beforeremove
21064         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
21065         * @param {Tree} tree The owner tree
21066         * @param {Node} parent The parent node
21067         * @param {Node} node The child node to be removed
21068         */
21069        "beforeremove" : true,
21070        /**
21071         * @event beforemove
21072         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
21073         * @param {Tree} tree The owner tree
21074         * @param {Node} node The node being moved
21075         * @param {Node} oldParent The parent of the node
21076         * @param {Node} newParent The new parent the node is moving to
21077         * @param {Number} index The index it is being moved to
21078         */
21079        "beforemove" : true,
21080        /**
21081         * @event beforeinsert
21082         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
21083         * @param {Tree} tree The owner tree
21084         * @param {Node} parent The parent node
21085         * @param {Node} node The child node to be inserted
21086         * @param {Node} refNode The child node the node is being inserted before
21087         */
21088        "beforeinsert" : true
21089    });
21090
21091     Roo.data.Tree.superclass.constructor.call(this);
21092 };
21093
21094 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
21095     pathSeparator: "/",
21096
21097     proxyNodeEvent : function(){
21098         return this.fireEvent.apply(this, arguments);
21099     },
21100
21101     /**
21102      * Returns the root node for this tree.
21103      * @return {Node}
21104      */
21105     getRootNode : function(){
21106         return this.root;
21107     },
21108
21109     /**
21110      * Sets the root node for this tree.
21111      * @param {Node} node
21112      * @return {Node}
21113      */
21114     setRootNode : function(node){
21115         this.root = node;
21116         node.ownerTree = this;
21117         node.isRoot = true;
21118         this.registerNode(node);
21119         return node;
21120     },
21121
21122     /**
21123      * Gets a node in this tree by its id.
21124      * @param {String} id
21125      * @return {Node}
21126      */
21127     getNodeById : function(id){
21128         return this.nodeHash[id];
21129     },
21130
21131     registerNode : function(node){
21132         this.nodeHash[node.id] = node;
21133     },
21134
21135     unregisterNode : function(node){
21136         delete this.nodeHash[node.id];
21137     },
21138
21139     toString : function(){
21140         return "[Tree"+(this.id?" "+this.id:"")+"]";
21141     }
21142 });
21143
21144 /**
21145  * @class Roo.data.Node
21146  * @extends Roo.util.Observable
21147  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
21148  * @cfg {String} id The id for this node. If one is not specified, one is generated.
21149  * @constructor
21150  * @param {Object} attributes The attributes/config for the node
21151  */
21152 Roo.data.Node = function(attributes){
21153     /**
21154      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
21155      * @type {Object}
21156      */
21157     this.attributes = attributes || {};
21158     this.leaf = this.attributes.leaf;
21159     /**
21160      * The node id. @type String
21161      */
21162     this.id = this.attributes.id;
21163     if(!this.id){
21164         this.id = Roo.id(null, "ynode-");
21165         this.attributes.id = this.id;
21166     }
21167     /**
21168      * All child nodes of this node. @type Array
21169      */
21170     this.childNodes = [];
21171     if(!this.childNodes.indexOf){ // indexOf is a must
21172         this.childNodes.indexOf = function(o){
21173             for(var i = 0, len = this.length; i < len; i++){
21174                 if(this[i] == o) {
21175                     return i;
21176                 }
21177             }
21178             return -1;
21179         };
21180     }
21181     /**
21182      * The parent node for this node. @type Node
21183      */
21184     this.parentNode = null;
21185     /**
21186      * The first direct child node of this node, or null if this node has no child nodes. @type Node
21187      */
21188     this.firstChild = null;
21189     /**
21190      * The last direct child node of this node, or null if this node has no child nodes. @type Node
21191      */
21192     this.lastChild = null;
21193     /**
21194      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
21195      */
21196     this.previousSibling = null;
21197     /**
21198      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
21199      */
21200     this.nextSibling = null;
21201
21202     this.addEvents({
21203        /**
21204         * @event append
21205         * Fires when a new child node is appended
21206         * @param {Tree} tree The owner tree
21207         * @param {Node} this This node
21208         * @param {Node} node The newly appended node
21209         * @param {Number} index The index of the newly appended node
21210         */
21211        "append" : true,
21212        /**
21213         * @event remove
21214         * Fires when a child node is removed
21215         * @param {Tree} tree The owner tree
21216         * @param {Node} this This node
21217         * @param {Node} node The removed node
21218         */
21219        "remove" : true,
21220        /**
21221         * @event move
21222         * Fires when this node is moved to a new location in the tree
21223         * @param {Tree} tree The owner tree
21224         * @param {Node} this This node
21225         * @param {Node} oldParent The old parent of this node
21226         * @param {Node} newParent The new parent of this node
21227         * @param {Number} index The index it was moved to
21228         */
21229        "move" : true,
21230        /**
21231         * @event insert
21232         * Fires when a new child node is inserted.
21233         * @param {Tree} tree The owner tree
21234         * @param {Node} this This node
21235         * @param {Node} node The child node inserted
21236         * @param {Node} refNode The child node the node was inserted before
21237         */
21238        "insert" : true,
21239        /**
21240         * @event beforeappend
21241         * Fires before a new child is appended, return false to cancel the append.
21242         * @param {Tree} tree The owner tree
21243         * @param {Node} this This node
21244         * @param {Node} node The child node to be appended
21245         */
21246        "beforeappend" : true,
21247        /**
21248         * @event beforeremove
21249         * Fires before a child is removed, return false to cancel the remove.
21250         * @param {Tree} tree The owner tree
21251         * @param {Node} this This node
21252         * @param {Node} node The child node to be removed
21253         */
21254        "beforeremove" : true,
21255        /**
21256         * @event beforemove
21257         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
21258         * @param {Tree} tree The owner tree
21259         * @param {Node} this This node
21260         * @param {Node} oldParent The parent of this node
21261         * @param {Node} newParent The new parent this node is moving to
21262         * @param {Number} index The index it is being moved to
21263         */
21264        "beforemove" : true,
21265        /**
21266         * @event beforeinsert
21267         * Fires before a new child is inserted, return false to cancel the insert.
21268         * @param {Tree} tree The owner tree
21269         * @param {Node} this This node
21270         * @param {Node} node The child node to be inserted
21271         * @param {Node} refNode The child node the node is being inserted before
21272         */
21273        "beforeinsert" : true
21274    });
21275     this.listeners = this.attributes.listeners;
21276     Roo.data.Node.superclass.constructor.call(this);
21277 };
21278
21279 Roo.extend(Roo.data.Node, Roo.util.Observable, {
21280     fireEvent : function(evtName){
21281         // first do standard event for this node
21282         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
21283             return false;
21284         }
21285         // then bubble it up to the tree if the event wasn't cancelled
21286         var ot = this.getOwnerTree();
21287         if(ot){
21288             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
21289                 return false;
21290             }
21291         }
21292         return true;
21293     },
21294
21295     /**
21296      * Returns true if this node is a leaf
21297      * @return {Boolean}
21298      */
21299     isLeaf : function(){
21300         return this.leaf === true;
21301     },
21302
21303     // private
21304     setFirstChild : function(node){
21305         this.firstChild = node;
21306     },
21307
21308     //private
21309     setLastChild : function(node){
21310         this.lastChild = node;
21311     },
21312
21313
21314     /**
21315      * Returns true if this node is the last child of its parent
21316      * @return {Boolean}
21317      */
21318     isLast : function(){
21319        return (!this.parentNode ? true : this.parentNode.lastChild == this);
21320     },
21321
21322     /**
21323      * Returns true if this node is the first child of its parent
21324      * @return {Boolean}
21325      */
21326     isFirst : function(){
21327        return (!this.parentNode ? true : this.parentNode.firstChild == this);
21328     },
21329
21330     hasChildNodes : function(){
21331         return !this.isLeaf() && this.childNodes.length > 0;
21332     },
21333
21334     /**
21335      * Insert node(s) as the last child node of this node.
21336      * @param {Node/Array} node The node or Array of nodes to append
21337      * @return {Node} The appended node if single append, or null if an array was passed
21338      */
21339     appendChild : function(node){
21340         var multi = false;
21341         if(node instanceof Array){
21342             multi = node;
21343         }else if(arguments.length > 1){
21344             multi = arguments;
21345         }
21346         // if passed an array or multiple args do them one by one
21347         if(multi){
21348             for(var i = 0, len = multi.length; i < len; i++) {
21349                 this.appendChild(multi[i]);
21350             }
21351         }else{
21352             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
21353                 return false;
21354             }
21355             var index = this.childNodes.length;
21356             var oldParent = node.parentNode;
21357             // it's a move, make sure we move it cleanly
21358             if(oldParent){
21359                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
21360                     return false;
21361                 }
21362                 oldParent.removeChild(node);
21363             }
21364             index = this.childNodes.length;
21365             if(index == 0){
21366                 this.setFirstChild(node);
21367             }
21368             this.childNodes.push(node);
21369             node.parentNode = this;
21370             var ps = this.childNodes[index-1];
21371             if(ps){
21372                 node.previousSibling = ps;
21373                 ps.nextSibling = node;
21374             }else{
21375                 node.previousSibling = null;
21376             }
21377             node.nextSibling = null;
21378             this.setLastChild(node);
21379             node.setOwnerTree(this.getOwnerTree());
21380             this.fireEvent("append", this.ownerTree, this, node, index);
21381             if(oldParent){
21382                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
21383             }
21384             return node;
21385         }
21386     },
21387
21388     /**
21389      * Removes a child node from this node.
21390      * @param {Node} node The node to remove
21391      * @return {Node} The removed node
21392      */
21393     removeChild : function(node){
21394         var index = this.childNodes.indexOf(node);
21395         if(index == -1){
21396             return false;
21397         }
21398         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
21399             return false;
21400         }
21401
21402         // remove it from childNodes collection
21403         this.childNodes.splice(index, 1);
21404
21405         // update siblings
21406         if(node.previousSibling){
21407             node.previousSibling.nextSibling = node.nextSibling;
21408         }
21409         if(node.nextSibling){
21410             node.nextSibling.previousSibling = node.previousSibling;
21411         }
21412
21413         // update child refs
21414         if(this.firstChild == node){
21415             this.setFirstChild(node.nextSibling);
21416         }
21417         if(this.lastChild == node){
21418             this.setLastChild(node.previousSibling);
21419         }
21420
21421         node.setOwnerTree(null);
21422         // clear any references from the node
21423         node.parentNode = null;
21424         node.previousSibling = null;
21425         node.nextSibling = null;
21426         this.fireEvent("remove", this.ownerTree, this, node);
21427         return node;
21428     },
21429
21430     /**
21431      * Inserts the first node before the second node in this nodes childNodes collection.
21432      * @param {Node} node The node to insert
21433      * @param {Node} refNode The node to insert before (if null the node is appended)
21434      * @return {Node} The inserted node
21435      */
21436     insertBefore : function(node, refNode){
21437         if(!refNode){ // like standard Dom, refNode can be null for append
21438             return this.appendChild(node);
21439         }
21440         // nothing to do
21441         if(node == refNode){
21442             return false;
21443         }
21444
21445         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
21446             return false;
21447         }
21448         var index = this.childNodes.indexOf(refNode);
21449         var oldParent = node.parentNode;
21450         var refIndex = index;
21451
21452         // when moving internally, indexes will change after remove
21453         if(oldParent == this && this.childNodes.indexOf(node) < index){
21454             refIndex--;
21455         }
21456
21457         // it's a move, make sure we move it cleanly
21458         if(oldParent){
21459             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
21460                 return false;
21461             }
21462             oldParent.removeChild(node);
21463         }
21464         if(refIndex == 0){
21465             this.setFirstChild(node);
21466         }
21467         this.childNodes.splice(refIndex, 0, node);
21468         node.parentNode = this;
21469         var ps = this.childNodes[refIndex-1];
21470         if(ps){
21471             node.previousSibling = ps;
21472             ps.nextSibling = node;
21473         }else{
21474             node.previousSibling = null;
21475         }
21476         node.nextSibling = refNode;
21477         refNode.previousSibling = node;
21478         node.setOwnerTree(this.getOwnerTree());
21479         this.fireEvent("insert", this.ownerTree, this, node, refNode);
21480         if(oldParent){
21481             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
21482         }
21483         return node;
21484     },
21485
21486     /**
21487      * Returns the child node at the specified index.
21488      * @param {Number} index
21489      * @return {Node}
21490      */
21491     item : function(index){
21492         return this.childNodes[index];
21493     },
21494
21495     /**
21496      * Replaces one child node in this node with another.
21497      * @param {Node} newChild The replacement node
21498      * @param {Node} oldChild The node to replace
21499      * @return {Node} The replaced node
21500      */
21501     replaceChild : function(newChild, oldChild){
21502         this.insertBefore(newChild, oldChild);
21503         this.removeChild(oldChild);
21504         return oldChild;
21505     },
21506
21507     /**
21508      * Returns the index of a child node
21509      * @param {Node} node
21510      * @return {Number} The index of the node or -1 if it was not found
21511      */
21512     indexOf : function(child){
21513         return this.childNodes.indexOf(child);
21514     },
21515
21516     /**
21517      * Returns the tree this node is in.
21518      * @return {Tree}
21519      */
21520     getOwnerTree : function(){
21521         // if it doesn't have one, look for one
21522         if(!this.ownerTree){
21523             var p = this;
21524             while(p){
21525                 if(p.ownerTree){
21526                     this.ownerTree = p.ownerTree;
21527                     break;
21528                 }
21529                 p = p.parentNode;
21530             }
21531         }
21532         return this.ownerTree;
21533     },
21534
21535     /**
21536      * Returns depth of this node (the root node has a depth of 0)
21537      * @return {Number}
21538      */
21539     getDepth : function(){
21540         var depth = 0;
21541         var p = this;
21542         while(p.parentNode){
21543             ++depth;
21544             p = p.parentNode;
21545         }
21546         return depth;
21547     },
21548
21549     // private
21550     setOwnerTree : function(tree){
21551         // if it's move, we need to update everyone
21552         if(tree != this.ownerTree){
21553             if(this.ownerTree){
21554                 this.ownerTree.unregisterNode(this);
21555             }
21556             this.ownerTree = tree;
21557             var cs = this.childNodes;
21558             for(var i = 0, len = cs.length; i < len; i++) {
21559                 cs[i].setOwnerTree(tree);
21560             }
21561             if(tree){
21562                 tree.registerNode(this);
21563             }
21564         }
21565     },
21566
21567     /**
21568      * Returns the path for this node. The path can be used to expand or select this node programmatically.
21569      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
21570      * @return {String} The path
21571      */
21572     getPath : function(attr){
21573         attr = attr || "id";
21574         var p = this.parentNode;
21575         var b = [this.attributes[attr]];
21576         while(p){
21577             b.unshift(p.attributes[attr]);
21578             p = p.parentNode;
21579         }
21580         var sep = this.getOwnerTree().pathSeparator;
21581         return sep + b.join(sep);
21582     },
21583
21584     /**
21585      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
21586      * function call will be the scope provided or the current node. The arguments to the function
21587      * will be the args provided or the current node. If the function returns false at any point,
21588      * the bubble is stopped.
21589      * @param {Function} fn The function to call
21590      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21591      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21592      */
21593     bubble : function(fn, scope, args){
21594         var p = this;
21595         while(p){
21596             if(fn.call(scope || p, args || p) === false){
21597                 break;
21598             }
21599             p = p.parentNode;
21600         }
21601     },
21602
21603     /**
21604      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
21605      * function call will be the scope provided or the current node. The arguments to the function
21606      * will be the args provided or the current node. If the function returns false at any point,
21607      * the cascade is stopped on that branch.
21608      * @param {Function} fn The function to call
21609      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21610      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21611      */
21612     cascade : function(fn, scope, args){
21613         if(fn.call(scope || this, args || this) !== false){
21614             var cs = this.childNodes;
21615             for(var i = 0, len = cs.length; i < len; i++) {
21616                 cs[i].cascade(fn, scope, args);
21617             }
21618         }
21619     },
21620
21621     /**
21622      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
21623      * function call will be the scope provided or the current node. The arguments to the function
21624      * will be the args provided or the current node. If the function returns false at any point,
21625      * the iteration stops.
21626      * @param {Function} fn The function to call
21627      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21628      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21629      */
21630     eachChild : function(fn, scope, args){
21631         var cs = this.childNodes;
21632         for(var i = 0, len = cs.length; i < len; i++) {
21633                 if(fn.call(scope || this, args || cs[i]) === false){
21634                     break;
21635                 }
21636         }
21637     },
21638
21639     /**
21640      * Finds the first child that has the attribute with the specified value.
21641      * @param {String} attribute The attribute name
21642      * @param {Mixed} value The value to search for
21643      * @return {Node} The found child or null if none was found
21644      */
21645     findChild : function(attribute, value){
21646         var cs = this.childNodes;
21647         for(var i = 0, len = cs.length; i < len; i++) {
21648                 if(cs[i].attributes[attribute] == value){
21649                     return cs[i];
21650                 }
21651         }
21652         return null;
21653     },
21654
21655     /**
21656      * Finds the first child by a custom function. The child matches if the function passed
21657      * returns true.
21658      * @param {Function} fn
21659      * @param {Object} scope (optional)
21660      * @return {Node} The found child or null if none was found
21661      */
21662     findChildBy : function(fn, scope){
21663         var cs = this.childNodes;
21664         for(var i = 0, len = cs.length; i < len; i++) {
21665                 if(fn.call(scope||cs[i], cs[i]) === true){
21666                     return cs[i];
21667                 }
21668         }
21669         return null;
21670     },
21671
21672     /**
21673      * Sorts this nodes children using the supplied sort function
21674      * @param {Function} fn
21675      * @param {Object} scope (optional)
21676      */
21677     sort : function(fn, scope){
21678         var cs = this.childNodes;
21679         var len = cs.length;
21680         if(len > 0){
21681             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
21682             cs.sort(sortFn);
21683             for(var i = 0; i < len; i++){
21684                 var n = cs[i];
21685                 n.previousSibling = cs[i-1];
21686                 n.nextSibling = cs[i+1];
21687                 if(i == 0){
21688                     this.setFirstChild(n);
21689                 }
21690                 if(i == len-1){
21691                     this.setLastChild(n);
21692                 }
21693             }
21694         }
21695     },
21696
21697     /**
21698      * Returns true if this node is an ancestor (at any point) of the passed node.
21699      * @param {Node} node
21700      * @return {Boolean}
21701      */
21702     contains : function(node){
21703         return node.isAncestor(this);
21704     },
21705
21706     /**
21707      * Returns true if the passed node is an ancestor (at any point) of this node.
21708      * @param {Node} node
21709      * @return {Boolean}
21710      */
21711     isAncestor : function(node){
21712         var p = this.parentNode;
21713         while(p){
21714             if(p == node){
21715                 return true;
21716             }
21717             p = p.parentNode;
21718         }
21719         return false;
21720     },
21721
21722     toString : function(){
21723         return "[Node"+(this.id?" "+this.id:"")+"]";
21724     }
21725 });/*
21726  * Based on:
21727  * Ext JS Library 1.1.1
21728  * Copyright(c) 2006-2007, Ext JS, LLC.
21729  *
21730  * Originally Released Under LGPL - original licence link has changed is not relivant.
21731  *
21732  * Fork - LGPL
21733  * <script type="text/javascript">
21734  */
21735  
21736
21737 /**
21738  * @class Roo.ComponentMgr
21739  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
21740  * @singleton
21741  */
21742 Roo.ComponentMgr = function(){
21743     var all = new Roo.util.MixedCollection();
21744
21745     return {
21746         /**
21747          * Registers a component.
21748          * @param {Roo.Component} c The component
21749          */
21750         register : function(c){
21751             all.add(c);
21752         },
21753
21754         /**
21755          * Unregisters a component.
21756          * @param {Roo.Component} c The component
21757          */
21758         unregister : function(c){
21759             all.remove(c);
21760         },
21761
21762         /**
21763          * Returns a component by id
21764          * @param {String} id The component id
21765          */
21766         get : function(id){
21767             return all.get(id);
21768         },
21769
21770         /**
21771          * Registers a function that will be called when a specified component is added to ComponentMgr
21772          * @param {String} id The component id
21773          * @param {Funtction} fn The callback function
21774          * @param {Object} scope The scope of the callback
21775          */
21776         onAvailable : function(id, fn, scope){
21777             all.on("add", function(index, o){
21778                 if(o.id == id){
21779                     fn.call(scope || o, o);
21780                     all.un("add", fn, scope);
21781                 }
21782             });
21783         }
21784     };
21785 }();/*
21786  * Based on:
21787  * Ext JS Library 1.1.1
21788  * Copyright(c) 2006-2007, Ext JS, LLC.
21789  *
21790  * Originally Released Under LGPL - original licence link has changed is not relivant.
21791  *
21792  * Fork - LGPL
21793  * <script type="text/javascript">
21794  */
21795  
21796 /**
21797  * @class Roo.Component
21798  * @extends Roo.util.Observable
21799  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
21800  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
21801  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
21802  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
21803  * All visual components (widgets) that require rendering into a layout should subclass Component.
21804  * @constructor
21805  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
21806  * 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
21807  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
21808  */
21809 Roo.Component = function(config){
21810     config = config || {};
21811     if(config.tagName || config.dom || typeof config == "string"){ // element object
21812         config = {el: config, id: config.id || config};
21813     }
21814     this.initialConfig = config;
21815
21816     Roo.apply(this, config);
21817     this.addEvents({
21818         /**
21819          * @event disable
21820          * Fires after the component is disabled.
21821              * @param {Roo.Component} this
21822              */
21823         disable : true,
21824         /**
21825          * @event enable
21826          * Fires after the component is enabled.
21827              * @param {Roo.Component} this
21828              */
21829         enable : true,
21830         /**
21831          * @event beforeshow
21832          * Fires before the component is shown.  Return false to stop the show.
21833              * @param {Roo.Component} this
21834              */
21835         beforeshow : true,
21836         /**
21837          * @event show
21838          * Fires after the component is shown.
21839              * @param {Roo.Component} this
21840              */
21841         show : true,
21842         /**
21843          * @event beforehide
21844          * Fires before the component is hidden. Return false to stop the hide.
21845              * @param {Roo.Component} this
21846              */
21847         beforehide : true,
21848         /**
21849          * @event hide
21850          * Fires after the component is hidden.
21851              * @param {Roo.Component} this
21852              */
21853         hide : true,
21854         /**
21855          * @event beforerender
21856          * Fires before the component is rendered. Return false to stop the render.
21857              * @param {Roo.Component} this
21858              */
21859         beforerender : true,
21860         /**
21861          * @event render
21862          * Fires after the component is rendered.
21863              * @param {Roo.Component} this
21864              */
21865         render : true,
21866         /**
21867          * @event beforedestroy
21868          * Fires before the component is destroyed. Return false to stop the destroy.
21869              * @param {Roo.Component} this
21870              */
21871         beforedestroy : true,
21872         /**
21873          * @event destroy
21874          * Fires after the component is destroyed.
21875              * @param {Roo.Component} this
21876              */
21877         destroy : true
21878     });
21879     if(!this.id){
21880         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
21881     }
21882     Roo.ComponentMgr.register(this);
21883     Roo.Component.superclass.constructor.call(this);
21884     this.initComponent();
21885     if(this.renderTo){ // not supported by all components yet. use at your own risk!
21886         this.render(this.renderTo);
21887         delete this.renderTo;
21888     }
21889 };
21890
21891 /** @private */
21892 Roo.Component.AUTO_ID = 1000;
21893
21894 Roo.extend(Roo.Component, Roo.util.Observable, {
21895     /**
21896      * @scope Roo.Component.prototype
21897      * @type {Boolean}
21898      * true if this component is hidden. Read-only.
21899      */
21900     hidden : false,
21901     /**
21902      * @type {Boolean}
21903      * true if this component is disabled. Read-only.
21904      */
21905     disabled : false,
21906     /**
21907      * @type {Boolean}
21908      * true if this component has been rendered. Read-only.
21909      */
21910     rendered : false,
21911     
21912     /** @cfg {String} disableClass
21913      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
21914      */
21915     disabledClass : "x-item-disabled",
21916         /** @cfg {Boolean} allowDomMove
21917          * Whether the component can move the Dom node when rendering (defaults to true).
21918          */
21919     allowDomMove : true,
21920     /** @cfg {String} hideMode
21921      * How this component should hidden. Supported values are
21922      * "visibility" (css visibility), "offsets" (negative offset position) and
21923      * "display" (css display) - defaults to "display".
21924      */
21925     hideMode: 'display',
21926
21927     /** @private */
21928     ctype : "Roo.Component",
21929
21930     /**
21931      * @cfg {String} actionMode 
21932      * which property holds the element that used for  hide() / show() / disable() / enable()
21933      * default is 'el' 
21934      */
21935     actionMode : "el",
21936
21937     /** @private */
21938     getActionEl : function(){
21939         return this[this.actionMode];
21940     },
21941
21942     initComponent : Roo.emptyFn,
21943     /**
21944      * If this is a lazy rendering component, render it to its container element.
21945      * @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.
21946      */
21947     render : function(container, position){
21948         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
21949             if(!container && this.el){
21950                 this.el = Roo.get(this.el);
21951                 container = this.el.dom.parentNode;
21952                 this.allowDomMove = false;
21953             }
21954             this.container = Roo.get(container);
21955             this.rendered = true;
21956             if(position !== undefined){
21957                 if(typeof position == 'number'){
21958                     position = this.container.dom.childNodes[position];
21959                 }else{
21960                     position = Roo.getDom(position);
21961                 }
21962             }
21963             this.onRender(this.container, position || null);
21964             if(this.cls){
21965                 this.el.addClass(this.cls);
21966                 delete this.cls;
21967             }
21968             if(this.style){
21969                 this.el.applyStyles(this.style);
21970                 delete this.style;
21971             }
21972             this.fireEvent("render", this);
21973             this.afterRender(this.container);
21974             if(this.hidden){
21975                 this.hide();
21976             }
21977             if(this.disabled){
21978                 this.disable();
21979             }
21980         }
21981         return this;
21982     },
21983
21984     /** @private */
21985     // default function is not really useful
21986     onRender : function(ct, position){
21987         if(this.el){
21988             this.el = Roo.get(this.el);
21989             if(this.allowDomMove !== false){
21990                 ct.dom.insertBefore(this.el.dom, position);
21991             }
21992         }
21993     },
21994
21995     /** @private */
21996     getAutoCreate : function(){
21997         var cfg = typeof this.autoCreate == "object" ?
21998                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
21999         if(this.id && !cfg.id){
22000             cfg.id = this.id;
22001         }
22002         return cfg;
22003     },
22004
22005     /** @private */
22006     afterRender : Roo.emptyFn,
22007
22008     /**
22009      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
22010      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
22011      */
22012     destroy : function(){
22013         if(this.fireEvent("beforedestroy", this) !== false){
22014             this.purgeListeners();
22015             this.beforeDestroy();
22016             if(this.rendered){
22017                 this.el.removeAllListeners();
22018                 this.el.remove();
22019                 if(this.actionMode == "container"){
22020                     this.container.remove();
22021                 }
22022             }
22023             this.onDestroy();
22024             Roo.ComponentMgr.unregister(this);
22025             this.fireEvent("destroy", this);
22026         }
22027     },
22028
22029         /** @private */
22030     beforeDestroy : function(){
22031
22032     },
22033
22034         /** @private */
22035         onDestroy : function(){
22036
22037     },
22038
22039     /**
22040      * Returns the underlying {@link Roo.Element}.
22041      * @return {Roo.Element} The element
22042      */
22043     getEl : function(){
22044         return this.el;
22045     },
22046
22047     /**
22048      * Returns the id of this component.
22049      * @return {String}
22050      */
22051     getId : function(){
22052         return this.id;
22053     },
22054
22055     /**
22056      * Try to focus this component.
22057      * @param {Boolean} selectText True to also select the text in this component (if applicable)
22058      * @return {Roo.Component} this
22059      */
22060     focus : function(selectText){
22061         if(this.rendered){
22062             this.el.focus();
22063             if(selectText === true){
22064                 this.el.dom.select();
22065             }
22066         }
22067         return this;
22068     },
22069
22070     /** @private */
22071     blur : function(){
22072         if(this.rendered){
22073             this.el.blur();
22074         }
22075         return this;
22076     },
22077
22078     /**
22079      * Disable this component.
22080      * @return {Roo.Component} this
22081      */
22082     disable : function(){
22083         if(this.rendered){
22084             this.onDisable();
22085         }
22086         this.disabled = true;
22087         this.fireEvent("disable", this);
22088         return this;
22089     },
22090
22091         // private
22092     onDisable : function(){
22093         this.getActionEl().addClass(this.disabledClass);
22094         this.el.dom.disabled = true;
22095     },
22096
22097     /**
22098      * Enable this component.
22099      * @return {Roo.Component} this
22100      */
22101     enable : function(){
22102         if(this.rendered){
22103             this.onEnable();
22104         }
22105         this.disabled = false;
22106         this.fireEvent("enable", this);
22107         return this;
22108     },
22109
22110         // private
22111     onEnable : function(){
22112         this.getActionEl().removeClass(this.disabledClass);
22113         this.el.dom.disabled = false;
22114     },
22115
22116     /**
22117      * Convenience function for setting disabled/enabled by boolean.
22118      * @param {Boolean} disabled
22119      */
22120     setDisabled : function(disabled){
22121         this[disabled ? "disable" : "enable"]();
22122     },
22123
22124     /**
22125      * Show this component.
22126      * @return {Roo.Component} this
22127      */
22128     show: function(){
22129         if(this.fireEvent("beforeshow", this) !== false){
22130             this.hidden = false;
22131             if(this.rendered){
22132                 this.onShow();
22133             }
22134             this.fireEvent("show", this);
22135         }
22136         return this;
22137     },
22138
22139     // private
22140     onShow : function(){
22141         var ae = this.getActionEl();
22142         if(this.hideMode == 'visibility'){
22143             ae.dom.style.visibility = "visible";
22144         }else if(this.hideMode == 'offsets'){
22145             ae.removeClass('x-hidden');
22146         }else{
22147             ae.dom.style.display = "";
22148         }
22149     },
22150
22151     /**
22152      * Hide this component.
22153      * @return {Roo.Component} this
22154      */
22155     hide: function(){
22156         if(this.fireEvent("beforehide", this) !== false){
22157             this.hidden = true;
22158             if(this.rendered){
22159                 this.onHide();
22160             }
22161             this.fireEvent("hide", this);
22162         }
22163         return this;
22164     },
22165
22166     // private
22167     onHide : function(){
22168         var ae = this.getActionEl();
22169         if(this.hideMode == 'visibility'){
22170             ae.dom.style.visibility = "hidden";
22171         }else if(this.hideMode == 'offsets'){
22172             ae.addClass('x-hidden');
22173         }else{
22174             ae.dom.style.display = "none";
22175         }
22176     },
22177
22178     /**
22179      * Convenience function to hide or show this component by boolean.
22180      * @param {Boolean} visible True to show, false to hide
22181      * @return {Roo.Component} this
22182      */
22183     setVisible: function(visible){
22184         if(visible) {
22185             this.show();
22186         }else{
22187             this.hide();
22188         }
22189         return this;
22190     },
22191
22192     /**
22193      * Returns true if this component is visible.
22194      */
22195     isVisible : function(){
22196         return this.getActionEl().isVisible();
22197     },
22198
22199     cloneConfig : function(overrides){
22200         overrides = overrides || {};
22201         var id = overrides.id || Roo.id();
22202         var cfg = Roo.applyIf(overrides, this.initialConfig);
22203         cfg.id = id; // prevent dup id
22204         return new this.constructor(cfg);
22205     }
22206 });/*
22207  * Based on:
22208  * Ext JS Library 1.1.1
22209  * Copyright(c) 2006-2007, Ext JS, LLC.
22210  *
22211  * Originally Released Under LGPL - original licence link has changed is not relivant.
22212  *
22213  * Fork - LGPL
22214  * <script type="text/javascript">
22215  */
22216  (function(){ 
22217 /**
22218  * @class Roo.Layer
22219  * @extends Roo.Element
22220  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
22221  * automatic maintaining of shadow/shim positions.
22222  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
22223  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
22224  * you can pass a string with a CSS class name. False turns off the shadow.
22225  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
22226  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
22227  * @cfg {String} cls CSS class to add to the element
22228  * @cfg {Number} zindex Starting z-index (defaults to 11000)
22229  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
22230  * @constructor
22231  * @param {Object} config An object with config options.
22232  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
22233  */
22234
22235 Roo.Layer = function(config, existingEl){
22236     config = config || {};
22237     var dh = Roo.DomHelper;
22238     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
22239     if(existingEl){
22240         this.dom = Roo.getDom(existingEl);
22241     }
22242     if(!this.dom){
22243         var o = config.dh || {tag: "div", cls: "x-layer"};
22244         this.dom = dh.append(pel, o);
22245     }
22246     if(config.cls){
22247         this.addClass(config.cls);
22248     }
22249     this.constrain = config.constrain !== false;
22250     this.visibilityMode = Roo.Element.VISIBILITY;
22251     if(config.id){
22252         this.id = this.dom.id = config.id;
22253     }else{
22254         this.id = Roo.id(this.dom);
22255     }
22256     this.zindex = config.zindex || this.getZIndex();
22257     this.position("absolute", this.zindex);
22258     if(config.shadow){
22259         this.shadowOffset = config.shadowOffset || 4;
22260         this.shadow = new Roo.Shadow({
22261             offset : this.shadowOffset,
22262             mode : config.shadow
22263         });
22264     }else{
22265         this.shadowOffset = 0;
22266     }
22267     this.useShim = config.shim !== false && Roo.useShims;
22268     this.useDisplay = config.useDisplay;
22269     this.hide();
22270 };
22271
22272 var supr = Roo.Element.prototype;
22273
22274 // shims are shared among layer to keep from having 100 iframes
22275 var shims = [];
22276
22277 Roo.extend(Roo.Layer, Roo.Element, {
22278
22279     getZIndex : function(){
22280         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
22281     },
22282
22283     getShim : function(){
22284         if(!this.useShim){
22285             return null;
22286         }
22287         if(this.shim){
22288             return this.shim;
22289         }
22290         var shim = shims.shift();
22291         if(!shim){
22292             shim = this.createShim();
22293             shim.enableDisplayMode('block');
22294             shim.dom.style.display = 'none';
22295             shim.dom.style.visibility = 'visible';
22296         }
22297         var pn = this.dom.parentNode;
22298         if(shim.dom.parentNode != pn){
22299             pn.insertBefore(shim.dom, this.dom);
22300         }
22301         shim.setStyle('z-index', this.getZIndex()-2);
22302         this.shim = shim;
22303         return shim;
22304     },
22305
22306     hideShim : function(){
22307         if(this.shim){
22308             this.shim.setDisplayed(false);
22309             shims.push(this.shim);
22310             delete this.shim;
22311         }
22312     },
22313
22314     disableShadow : function(){
22315         if(this.shadow){
22316             this.shadowDisabled = true;
22317             this.shadow.hide();
22318             this.lastShadowOffset = this.shadowOffset;
22319             this.shadowOffset = 0;
22320         }
22321     },
22322
22323     enableShadow : function(show){
22324         if(this.shadow){
22325             this.shadowDisabled = false;
22326             this.shadowOffset = this.lastShadowOffset;
22327             delete this.lastShadowOffset;
22328             if(show){
22329                 this.sync(true);
22330             }
22331         }
22332     },
22333
22334     // private
22335     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
22336     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
22337     sync : function(doShow){
22338         var sw = this.shadow;
22339         if(!this.updating && this.isVisible() && (sw || this.useShim)){
22340             var sh = this.getShim();
22341
22342             var w = this.getWidth(),
22343                 h = this.getHeight();
22344
22345             var l = this.getLeft(true),
22346                 t = this.getTop(true);
22347
22348             if(sw && !this.shadowDisabled){
22349                 if(doShow && !sw.isVisible()){
22350                     sw.show(this);
22351                 }else{
22352                     sw.realign(l, t, w, h);
22353                 }
22354                 if(sh){
22355                     if(doShow){
22356                        sh.show();
22357                     }
22358                     // fit the shim behind the shadow, so it is shimmed too
22359                     var a = sw.adjusts, s = sh.dom.style;
22360                     s.left = (Math.min(l, l+a.l))+"px";
22361                     s.top = (Math.min(t, t+a.t))+"px";
22362                     s.width = (w+a.w)+"px";
22363                     s.height = (h+a.h)+"px";
22364                 }
22365             }else if(sh){
22366                 if(doShow){
22367                    sh.show();
22368                 }
22369                 sh.setSize(w, h);
22370                 sh.setLeftTop(l, t);
22371             }
22372             
22373         }
22374     },
22375
22376     // private
22377     destroy : function(){
22378         this.hideShim();
22379         if(this.shadow){
22380             this.shadow.hide();
22381         }
22382         this.removeAllListeners();
22383         var pn = this.dom.parentNode;
22384         if(pn){
22385             pn.removeChild(this.dom);
22386         }
22387         Roo.Element.uncache(this.id);
22388     },
22389
22390     remove : function(){
22391         this.destroy();
22392     },
22393
22394     // private
22395     beginUpdate : function(){
22396         this.updating = true;
22397     },
22398
22399     // private
22400     endUpdate : function(){
22401         this.updating = false;
22402         this.sync(true);
22403     },
22404
22405     // private
22406     hideUnders : function(negOffset){
22407         if(this.shadow){
22408             this.shadow.hide();
22409         }
22410         this.hideShim();
22411     },
22412
22413     // private
22414     constrainXY : function(){
22415         if(this.constrain){
22416             var vw = Roo.lib.Dom.getViewWidth(),
22417                 vh = Roo.lib.Dom.getViewHeight();
22418             var s = Roo.get(document).getScroll();
22419
22420             var xy = this.getXY();
22421             var x = xy[0], y = xy[1];   
22422             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
22423             // only move it if it needs it
22424             var moved = false;
22425             // first validate right/bottom
22426             if((x + w) > vw+s.left){
22427                 x = vw - w - this.shadowOffset;
22428                 moved = true;
22429             }
22430             if((y + h) > vh+s.top){
22431                 y = vh - h - this.shadowOffset;
22432                 moved = true;
22433             }
22434             // then make sure top/left isn't negative
22435             if(x < s.left){
22436                 x = s.left;
22437                 moved = true;
22438             }
22439             if(y < s.top){
22440                 y = s.top;
22441                 moved = true;
22442             }
22443             if(moved){
22444                 if(this.avoidY){
22445                     var ay = this.avoidY;
22446                     if(y <= ay && (y+h) >= ay){
22447                         y = ay-h-5;   
22448                     }
22449                 }
22450                 xy = [x, y];
22451                 this.storeXY(xy);
22452                 supr.setXY.call(this, xy);
22453                 this.sync();
22454             }
22455         }
22456     },
22457
22458     isVisible : function(){
22459         return this.visible;    
22460     },
22461
22462     // private
22463     showAction : function(){
22464         this.visible = true; // track visibility to prevent getStyle calls
22465         if(this.useDisplay === true){
22466             this.setDisplayed("");
22467         }else if(this.lastXY){
22468             supr.setXY.call(this, this.lastXY);
22469         }else if(this.lastLT){
22470             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
22471         }
22472     },
22473
22474     // private
22475     hideAction : function(){
22476         this.visible = false;
22477         if(this.useDisplay === true){
22478             this.setDisplayed(false);
22479         }else{
22480             this.setLeftTop(-10000,-10000);
22481         }
22482     },
22483
22484     // overridden Element method
22485     setVisible : function(v, a, d, c, e){
22486         if(v){
22487             this.showAction();
22488         }
22489         if(a && v){
22490             var cb = function(){
22491                 this.sync(true);
22492                 if(c){
22493                     c();
22494                 }
22495             }.createDelegate(this);
22496             supr.setVisible.call(this, true, true, d, cb, e);
22497         }else{
22498             if(!v){
22499                 this.hideUnders(true);
22500             }
22501             var cb = c;
22502             if(a){
22503                 cb = function(){
22504                     this.hideAction();
22505                     if(c){
22506                         c();
22507                     }
22508                 }.createDelegate(this);
22509             }
22510             supr.setVisible.call(this, v, a, d, cb, e);
22511             if(v){
22512                 this.sync(true);
22513             }else if(!a){
22514                 this.hideAction();
22515             }
22516         }
22517     },
22518
22519     storeXY : function(xy){
22520         delete this.lastLT;
22521         this.lastXY = xy;
22522     },
22523
22524     storeLeftTop : function(left, top){
22525         delete this.lastXY;
22526         this.lastLT = [left, top];
22527     },
22528
22529     // private
22530     beforeFx : function(){
22531         this.beforeAction();
22532         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
22533     },
22534
22535     // private
22536     afterFx : function(){
22537         Roo.Layer.superclass.afterFx.apply(this, arguments);
22538         this.sync(this.isVisible());
22539     },
22540
22541     // private
22542     beforeAction : function(){
22543         if(!this.updating && this.shadow){
22544             this.shadow.hide();
22545         }
22546     },
22547
22548     // overridden Element method
22549     setLeft : function(left){
22550         this.storeLeftTop(left, this.getTop(true));
22551         supr.setLeft.apply(this, arguments);
22552         this.sync();
22553     },
22554
22555     setTop : function(top){
22556         this.storeLeftTop(this.getLeft(true), top);
22557         supr.setTop.apply(this, arguments);
22558         this.sync();
22559     },
22560
22561     setLeftTop : function(left, top){
22562         this.storeLeftTop(left, top);
22563         supr.setLeftTop.apply(this, arguments);
22564         this.sync();
22565     },
22566
22567     setXY : function(xy, a, d, c, e){
22568         this.fixDisplay();
22569         this.beforeAction();
22570         this.storeXY(xy);
22571         var cb = this.createCB(c);
22572         supr.setXY.call(this, xy, a, d, cb, e);
22573         if(!a){
22574             cb();
22575         }
22576     },
22577
22578     // private
22579     createCB : function(c){
22580         var el = this;
22581         return function(){
22582             el.constrainXY();
22583             el.sync(true);
22584             if(c){
22585                 c();
22586             }
22587         };
22588     },
22589
22590     // overridden Element method
22591     setX : function(x, a, d, c, e){
22592         this.setXY([x, this.getY()], a, d, c, e);
22593     },
22594
22595     // overridden Element method
22596     setY : function(y, a, d, c, e){
22597         this.setXY([this.getX(), y], a, d, c, e);
22598     },
22599
22600     // overridden Element method
22601     setSize : function(w, h, a, d, c, e){
22602         this.beforeAction();
22603         var cb = this.createCB(c);
22604         supr.setSize.call(this, w, h, a, d, cb, e);
22605         if(!a){
22606             cb();
22607         }
22608     },
22609
22610     // overridden Element method
22611     setWidth : function(w, a, d, c, e){
22612         this.beforeAction();
22613         var cb = this.createCB(c);
22614         supr.setWidth.call(this, w, a, d, cb, e);
22615         if(!a){
22616             cb();
22617         }
22618     },
22619
22620     // overridden Element method
22621     setHeight : function(h, a, d, c, e){
22622         this.beforeAction();
22623         var cb = this.createCB(c);
22624         supr.setHeight.call(this, h, a, d, cb, e);
22625         if(!a){
22626             cb();
22627         }
22628     },
22629
22630     // overridden Element method
22631     setBounds : function(x, y, w, h, a, d, c, e){
22632         this.beforeAction();
22633         var cb = this.createCB(c);
22634         if(!a){
22635             this.storeXY([x, y]);
22636             supr.setXY.call(this, [x, y]);
22637             supr.setSize.call(this, w, h, a, d, cb, e);
22638             cb();
22639         }else{
22640             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
22641         }
22642         return this;
22643     },
22644     
22645     /**
22646      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
22647      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
22648      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
22649      * @param {Number} zindex The new z-index to set
22650      * @return {this} The Layer
22651      */
22652     setZIndex : function(zindex){
22653         this.zindex = zindex;
22654         this.setStyle("z-index", zindex + 2);
22655         if(this.shadow){
22656             this.shadow.setZIndex(zindex + 1);
22657         }
22658         if(this.shim){
22659             this.shim.setStyle("z-index", zindex);
22660         }
22661     }
22662 });
22663 })();/*
22664  * Based on:
22665  * Ext JS Library 1.1.1
22666  * Copyright(c) 2006-2007, Ext JS, LLC.
22667  *
22668  * Originally Released Under LGPL - original licence link has changed is not relivant.
22669  *
22670  * Fork - LGPL
22671  * <script type="text/javascript">
22672  */
22673
22674
22675 /**
22676  * @class Roo.Shadow
22677  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
22678  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
22679  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
22680  * @constructor
22681  * Create a new Shadow
22682  * @param {Object} config The config object
22683  */
22684 Roo.Shadow = function(config){
22685     Roo.apply(this, config);
22686     if(typeof this.mode != "string"){
22687         this.mode = this.defaultMode;
22688     }
22689     var o = this.offset, a = {h: 0};
22690     var rad = Math.floor(this.offset/2);
22691     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
22692         case "drop":
22693             a.w = 0;
22694             a.l = a.t = o;
22695             a.t -= 1;
22696             if(Roo.isIE){
22697                 a.l -= this.offset + rad;
22698                 a.t -= this.offset + rad;
22699                 a.w -= rad;
22700                 a.h -= rad;
22701                 a.t += 1;
22702             }
22703         break;
22704         case "sides":
22705             a.w = (o*2);
22706             a.l = -o;
22707             a.t = o-1;
22708             if(Roo.isIE){
22709                 a.l -= (this.offset - rad);
22710                 a.t -= this.offset + rad;
22711                 a.l += 1;
22712                 a.w -= (this.offset - rad)*2;
22713                 a.w -= rad + 1;
22714                 a.h -= 1;
22715             }
22716         break;
22717         case "frame":
22718             a.w = a.h = (o*2);
22719             a.l = a.t = -o;
22720             a.t += 1;
22721             a.h -= 2;
22722             if(Roo.isIE){
22723                 a.l -= (this.offset - rad);
22724                 a.t -= (this.offset - rad);
22725                 a.l += 1;
22726                 a.w -= (this.offset + rad + 1);
22727                 a.h -= (this.offset + rad);
22728                 a.h += 1;
22729             }
22730         break;
22731     };
22732
22733     this.adjusts = a;
22734 };
22735
22736 Roo.Shadow.prototype = {
22737     /**
22738      * @cfg {String} mode
22739      * The shadow display mode.  Supports the following options:<br />
22740      * sides: Shadow displays on both sides and bottom only<br />
22741      * frame: Shadow displays equally on all four sides<br />
22742      * drop: Traditional bottom-right drop shadow (default)
22743      */
22744     /**
22745      * @cfg {String} offset
22746      * The number of pixels to offset the shadow from the element (defaults to 4)
22747      */
22748     offset: 4,
22749
22750     // private
22751     defaultMode: "drop",
22752
22753     /**
22754      * Displays the shadow under the target element
22755      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
22756      */
22757     show : function(target){
22758         target = Roo.get(target);
22759         if(!this.el){
22760             this.el = Roo.Shadow.Pool.pull();
22761             if(this.el.dom.nextSibling != target.dom){
22762                 this.el.insertBefore(target);
22763             }
22764         }
22765         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
22766         if(Roo.isIE){
22767             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
22768         }
22769         this.realign(
22770             target.getLeft(true),
22771             target.getTop(true),
22772             target.getWidth(),
22773             target.getHeight()
22774         );
22775         this.el.dom.style.display = "block";
22776     },
22777
22778     /**
22779      * Returns true if the shadow is visible, else false
22780      */
22781     isVisible : function(){
22782         return this.el ? true : false;  
22783     },
22784
22785     /**
22786      * Direct alignment when values are already available. Show must be called at least once before
22787      * calling this method to ensure it is initialized.
22788      * @param {Number} left The target element left position
22789      * @param {Number} top The target element top position
22790      * @param {Number} width The target element width
22791      * @param {Number} height The target element height
22792      */
22793     realign : function(l, t, w, h){
22794         if(!this.el){
22795             return;
22796         }
22797         var a = this.adjusts, d = this.el.dom, s = d.style;
22798         var iea = 0;
22799         s.left = (l+a.l)+"px";
22800         s.top = (t+a.t)+"px";
22801         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
22802  
22803         if(s.width != sws || s.height != shs){
22804             s.width = sws;
22805             s.height = shs;
22806             if(!Roo.isIE){
22807                 var cn = d.childNodes;
22808                 var sww = Math.max(0, (sw-12))+"px";
22809                 cn[0].childNodes[1].style.width = sww;
22810                 cn[1].childNodes[1].style.width = sww;
22811                 cn[2].childNodes[1].style.width = sww;
22812                 cn[1].style.height = Math.max(0, (sh-12))+"px";
22813             }
22814         }
22815     },
22816
22817     /**
22818      * Hides this shadow
22819      */
22820     hide : function(){
22821         if(this.el){
22822             this.el.dom.style.display = "none";
22823             Roo.Shadow.Pool.push(this.el);
22824             delete this.el;
22825         }
22826     },
22827
22828     /**
22829      * Adjust the z-index of this shadow
22830      * @param {Number} zindex The new z-index
22831      */
22832     setZIndex : function(z){
22833         this.zIndex = z;
22834         if(this.el){
22835             this.el.setStyle("z-index", z);
22836         }
22837     }
22838 };
22839
22840 // Private utility class that manages the internal Shadow cache
22841 Roo.Shadow.Pool = function(){
22842     var p = [];
22843     var markup = Roo.isIE ?
22844                  '<div class="x-ie-shadow"></div>' :
22845                  '<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>';
22846     return {
22847         pull : function(){
22848             var sh = p.shift();
22849             if(!sh){
22850                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
22851                 sh.autoBoxAdjust = false;
22852             }
22853             return sh;
22854         },
22855
22856         push : function(sh){
22857             p.push(sh);
22858         }
22859     };
22860 }();/*
22861  * Based on:
22862  * Ext JS Library 1.1.1
22863  * Copyright(c) 2006-2007, Ext JS, LLC.
22864  *
22865  * Originally Released Under LGPL - original licence link has changed is not relivant.
22866  *
22867  * Fork - LGPL
22868  * <script type="text/javascript">
22869  */
22870
22871 /**
22872  * @class Roo.BoxComponent
22873  * @extends Roo.Component
22874  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
22875  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
22876  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
22877  * layout containers.
22878  * @constructor
22879  * @param {Roo.Element/String/Object} config The configuration options.
22880  */
22881 Roo.BoxComponent = function(config){
22882     Roo.Component.call(this, config);
22883     this.addEvents({
22884         /**
22885          * @event resize
22886          * Fires after the component is resized.
22887              * @param {Roo.Component} this
22888              * @param {Number} adjWidth The box-adjusted width that was set
22889              * @param {Number} adjHeight The box-adjusted height that was set
22890              * @param {Number} rawWidth The width that was originally specified
22891              * @param {Number} rawHeight The height that was originally specified
22892              */
22893         resize : true,
22894         /**
22895          * @event move
22896          * Fires after the component is moved.
22897              * @param {Roo.Component} this
22898              * @param {Number} x The new x position
22899              * @param {Number} y The new y position
22900              */
22901         move : true
22902     });
22903 };
22904
22905 Roo.extend(Roo.BoxComponent, Roo.Component, {
22906     // private, set in afterRender to signify that the component has been rendered
22907     boxReady : false,
22908     // private, used to defer height settings to subclasses
22909     deferHeight: false,
22910     /** @cfg {Number} width
22911      * width (optional) size of component
22912      */
22913      /** @cfg {Number} height
22914      * height (optional) size of component
22915      */
22916      
22917     /**
22918      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
22919      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
22920      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
22921      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
22922      * @return {Roo.BoxComponent} this
22923      */
22924     setSize : function(w, h){
22925         // support for standard size objects
22926         if(typeof w == 'object'){
22927             h = w.height;
22928             w = w.width;
22929         }
22930         // not rendered
22931         if(!this.boxReady){
22932             this.width = w;
22933             this.height = h;
22934             return this;
22935         }
22936
22937         // prevent recalcs when not needed
22938         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
22939             return this;
22940         }
22941         this.lastSize = {width: w, height: h};
22942
22943         var adj = this.adjustSize(w, h);
22944         var aw = adj.width, ah = adj.height;
22945         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
22946             var rz = this.getResizeEl();
22947             if(!this.deferHeight && aw !== undefined && ah !== undefined){
22948                 rz.setSize(aw, ah);
22949             }else if(!this.deferHeight && ah !== undefined){
22950                 rz.setHeight(ah);
22951             }else if(aw !== undefined){
22952                 rz.setWidth(aw);
22953             }
22954             this.onResize(aw, ah, w, h);
22955             this.fireEvent('resize', this, aw, ah, w, h);
22956         }
22957         return this;
22958     },
22959
22960     /**
22961      * Gets the current size of the component's underlying element.
22962      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
22963      */
22964     getSize : function(){
22965         return this.el.getSize();
22966     },
22967
22968     /**
22969      * Gets the current XY position 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      * @return {Array} The XY position of the element (e.g., [100, 200])
22972      */
22973     getPosition : function(local){
22974         if(local === true){
22975             return [this.el.getLeft(true), this.el.getTop(true)];
22976         }
22977         return this.xy || this.el.getXY();
22978     },
22979
22980     /**
22981      * Gets the current box measurements of the component's underlying element.
22982      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
22983      * @returns {Object} box An object in the format {x, y, width, height}
22984      */
22985     getBox : function(local){
22986         var s = this.el.getSize();
22987         if(local){
22988             s.x = this.el.getLeft(true);
22989             s.y = this.el.getTop(true);
22990         }else{
22991             var xy = this.xy || this.el.getXY();
22992             s.x = xy[0];
22993             s.y = xy[1];
22994         }
22995         return s;
22996     },
22997
22998     /**
22999      * Sets the current box measurements of the component's underlying element.
23000      * @param {Object} box An object in the format {x, y, width, height}
23001      * @returns {Roo.BoxComponent} this
23002      */
23003     updateBox : function(box){
23004         this.setSize(box.width, box.height);
23005         this.setPagePosition(box.x, box.y);
23006         return this;
23007     },
23008
23009     // protected
23010     getResizeEl : function(){
23011         return this.resizeEl || this.el;
23012     },
23013
23014     // protected
23015     getPositionEl : function(){
23016         return this.positionEl || this.el;
23017     },
23018
23019     /**
23020      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
23021      * This method fires the move event.
23022      * @param {Number} left The new left
23023      * @param {Number} top The new top
23024      * @returns {Roo.BoxComponent} this
23025      */
23026     setPosition : function(x, y){
23027         this.x = x;
23028         this.y = y;
23029         if(!this.boxReady){
23030             return this;
23031         }
23032         var adj = this.adjustPosition(x, y);
23033         var ax = adj.x, ay = adj.y;
23034
23035         var el = this.getPositionEl();
23036         if(ax !== undefined || ay !== undefined){
23037             if(ax !== undefined && ay !== undefined){
23038                 el.setLeftTop(ax, ay);
23039             }else if(ax !== undefined){
23040                 el.setLeft(ax);
23041             }else if(ay !== undefined){
23042                 el.setTop(ay);
23043             }
23044             this.onPosition(ax, ay);
23045             this.fireEvent('move', this, ax, ay);
23046         }
23047         return this;
23048     },
23049
23050     /**
23051      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
23052      * This method fires the move event.
23053      * @param {Number} x The new x position
23054      * @param {Number} y The new y position
23055      * @returns {Roo.BoxComponent} this
23056      */
23057     setPagePosition : function(x, y){
23058         this.pageX = x;
23059         this.pageY = y;
23060         if(!this.boxReady){
23061             return;
23062         }
23063         if(x === undefined || y === undefined){ // cannot translate undefined points
23064             return;
23065         }
23066         var p = this.el.translatePoints(x, y);
23067         this.setPosition(p.left, p.top);
23068         return this;
23069     },
23070
23071     // private
23072     onRender : function(ct, position){
23073         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
23074         if(this.resizeEl){
23075             this.resizeEl = Roo.get(this.resizeEl);
23076         }
23077         if(this.positionEl){
23078             this.positionEl = Roo.get(this.positionEl);
23079         }
23080     },
23081
23082     // private
23083     afterRender : function(){
23084         Roo.BoxComponent.superclass.afterRender.call(this);
23085         this.boxReady = true;
23086         this.setSize(this.width, this.height);
23087         if(this.x || this.y){
23088             this.setPosition(this.x, this.y);
23089         }
23090         if(this.pageX || this.pageY){
23091             this.setPagePosition(this.pageX, this.pageY);
23092         }
23093     },
23094
23095     /**
23096      * Force the component's size to recalculate based on the underlying element's current height and width.
23097      * @returns {Roo.BoxComponent} this
23098      */
23099     syncSize : function(){
23100         delete this.lastSize;
23101         this.setSize(this.el.getWidth(), this.el.getHeight());
23102         return this;
23103     },
23104
23105     /**
23106      * Called after the component is resized, this method is empty by default but can be implemented by any
23107      * subclass that needs to perform custom logic after a resize occurs.
23108      * @param {Number} adjWidth The box-adjusted width that was set
23109      * @param {Number} adjHeight The box-adjusted height that was set
23110      * @param {Number} rawWidth The width that was originally specified
23111      * @param {Number} rawHeight The height that was originally specified
23112      */
23113     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
23114
23115     },
23116
23117     /**
23118      * Called after the component is moved, this method is empty by default but can be implemented by any
23119      * subclass that needs to perform custom logic after a move occurs.
23120      * @param {Number} x The new x position
23121      * @param {Number} y The new y position
23122      */
23123     onPosition : function(x, y){
23124
23125     },
23126
23127     // private
23128     adjustSize : function(w, h){
23129         if(this.autoWidth){
23130             w = 'auto';
23131         }
23132         if(this.autoHeight){
23133             h = 'auto';
23134         }
23135         return {width : w, height: h};
23136     },
23137
23138     // private
23139     adjustPosition : function(x, y){
23140         return {x : x, y: y};
23141     }
23142 });/*
23143  * Based on:
23144  * Ext JS Library 1.1.1
23145  * Copyright(c) 2006-2007, Ext JS, LLC.
23146  *
23147  * Originally Released Under LGPL - original licence link has changed is not relivant.
23148  *
23149  * Fork - LGPL
23150  * <script type="text/javascript">
23151  */
23152
23153
23154 /**
23155  * @class Roo.SplitBar
23156  * @extends Roo.util.Observable
23157  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
23158  * <br><br>
23159  * Usage:
23160  * <pre><code>
23161 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
23162                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
23163 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
23164 split.minSize = 100;
23165 split.maxSize = 600;
23166 split.animate = true;
23167 split.on('moved', splitterMoved);
23168 </code></pre>
23169  * @constructor
23170  * Create a new SplitBar
23171  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
23172  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
23173  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
23174  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
23175                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
23176                         position of the SplitBar).
23177  */
23178 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
23179     
23180     /** @private */
23181     this.el = Roo.get(dragElement, true);
23182     this.el.dom.unselectable = "on";
23183     /** @private */
23184     this.resizingEl = Roo.get(resizingElement, true);
23185
23186     /**
23187      * @private
23188      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
23189      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
23190      * @type Number
23191      */
23192     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
23193     
23194     /**
23195      * The minimum size of the resizing element. (Defaults to 0)
23196      * @type Number
23197      */
23198     this.minSize = 0;
23199     
23200     /**
23201      * The maximum size of the resizing element. (Defaults to 2000)
23202      * @type Number
23203      */
23204     this.maxSize = 2000;
23205     
23206     /**
23207      * Whether to animate the transition to the new size
23208      * @type Boolean
23209      */
23210     this.animate = false;
23211     
23212     /**
23213      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
23214      * @type Boolean
23215      */
23216     this.useShim = false;
23217     
23218     /** @private */
23219     this.shim = null;
23220     
23221     if(!existingProxy){
23222         /** @private */
23223         this.proxy = Roo.SplitBar.createProxy(this.orientation);
23224     }else{
23225         this.proxy = Roo.get(existingProxy).dom;
23226     }
23227     /** @private */
23228     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
23229     
23230     /** @private */
23231     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
23232     
23233     /** @private */
23234     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
23235     
23236     /** @private */
23237     this.dragSpecs = {};
23238     
23239     /**
23240      * @private The adapter to use to positon and resize elements
23241      */
23242     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
23243     this.adapter.init(this);
23244     
23245     if(this.orientation == Roo.SplitBar.HORIZONTAL){
23246         /** @private */
23247         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
23248         this.el.addClass("x-splitbar-h");
23249     }else{
23250         /** @private */
23251         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
23252         this.el.addClass("x-splitbar-v");
23253     }
23254     
23255     this.addEvents({
23256         /**
23257          * @event resize
23258          * Fires when the splitter is moved (alias for {@link #event-moved})
23259          * @param {Roo.SplitBar} this
23260          * @param {Number} newSize the new width or height
23261          */
23262         "resize" : true,
23263         /**
23264          * @event moved
23265          * Fires when the splitter is moved
23266          * @param {Roo.SplitBar} this
23267          * @param {Number} newSize the new width or height
23268          */
23269         "moved" : true,
23270         /**
23271          * @event beforeresize
23272          * Fires before the splitter is dragged
23273          * @param {Roo.SplitBar} this
23274          */
23275         "beforeresize" : true,
23276
23277         "beforeapply" : true
23278     });
23279
23280     Roo.util.Observable.call(this);
23281 };
23282
23283 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
23284     onStartProxyDrag : function(x, y){
23285         this.fireEvent("beforeresize", this);
23286         if(!this.overlay){
23287             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
23288             o.unselectable();
23289             o.enableDisplayMode("block");
23290             // all splitbars share the same overlay
23291             Roo.SplitBar.prototype.overlay = o;
23292         }
23293         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
23294         this.overlay.show();
23295         Roo.get(this.proxy).setDisplayed("block");
23296         var size = this.adapter.getElementSize(this);
23297         this.activeMinSize = this.getMinimumSize();;
23298         this.activeMaxSize = this.getMaximumSize();;
23299         var c1 = size - this.activeMinSize;
23300         var c2 = Math.max(this.activeMaxSize - size, 0);
23301         if(this.orientation == Roo.SplitBar.HORIZONTAL){
23302             this.dd.resetConstraints();
23303             this.dd.setXConstraint(
23304                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
23305                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
23306             );
23307             this.dd.setYConstraint(0, 0);
23308         }else{
23309             this.dd.resetConstraints();
23310             this.dd.setXConstraint(0, 0);
23311             this.dd.setYConstraint(
23312                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
23313                 this.placement == Roo.SplitBar.TOP ? c2 : c1
23314             );
23315          }
23316         this.dragSpecs.startSize = size;
23317         this.dragSpecs.startPoint = [x, y];
23318         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
23319     },
23320     
23321     /** 
23322      * @private Called after the drag operation by the DDProxy
23323      */
23324     onEndProxyDrag : function(e){
23325         Roo.get(this.proxy).setDisplayed(false);
23326         var endPoint = Roo.lib.Event.getXY(e);
23327         if(this.overlay){
23328             this.overlay.hide();
23329         }
23330         var newSize;
23331         if(this.orientation == Roo.SplitBar.HORIZONTAL){
23332             newSize = this.dragSpecs.startSize + 
23333                 (this.placement == Roo.SplitBar.LEFT ?
23334                     endPoint[0] - this.dragSpecs.startPoint[0] :
23335                     this.dragSpecs.startPoint[0] - endPoint[0]
23336                 );
23337         }else{
23338             newSize = this.dragSpecs.startSize + 
23339                 (this.placement == Roo.SplitBar.TOP ?
23340                     endPoint[1] - this.dragSpecs.startPoint[1] :
23341                     this.dragSpecs.startPoint[1] - endPoint[1]
23342                 );
23343         }
23344         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
23345         if(newSize != this.dragSpecs.startSize){
23346             if(this.fireEvent('beforeapply', this, newSize) !== false){
23347                 this.adapter.setElementSize(this, newSize);
23348                 this.fireEvent("moved", this, newSize);
23349                 this.fireEvent("resize", this, newSize);
23350             }
23351         }
23352     },
23353     
23354     /**
23355      * Get the adapter this SplitBar uses
23356      * @return The adapter object
23357      */
23358     getAdapter : function(){
23359         return this.adapter;
23360     },
23361     
23362     /**
23363      * Set the adapter this SplitBar uses
23364      * @param {Object} adapter A SplitBar adapter object
23365      */
23366     setAdapter : function(adapter){
23367         this.adapter = adapter;
23368         this.adapter.init(this);
23369     },
23370     
23371     /**
23372      * Gets the minimum size for the resizing element
23373      * @return {Number} The minimum size
23374      */
23375     getMinimumSize : function(){
23376         return this.minSize;
23377     },
23378     
23379     /**
23380      * Sets the minimum size for the resizing element
23381      * @param {Number} minSize The minimum size
23382      */
23383     setMinimumSize : function(minSize){
23384         this.minSize = minSize;
23385     },
23386     
23387     /**
23388      * Gets the maximum size for the resizing element
23389      * @return {Number} The maximum size
23390      */
23391     getMaximumSize : function(){
23392         return this.maxSize;
23393     },
23394     
23395     /**
23396      * Sets the maximum size for the resizing element
23397      * @param {Number} maxSize The maximum size
23398      */
23399     setMaximumSize : function(maxSize){
23400         this.maxSize = maxSize;
23401     },
23402     
23403     /**
23404      * Sets the initialize size for the resizing element
23405      * @param {Number} size The initial size
23406      */
23407     setCurrentSize : function(size){
23408         var oldAnimate = this.animate;
23409         this.animate = false;
23410         this.adapter.setElementSize(this, size);
23411         this.animate = oldAnimate;
23412     },
23413     
23414     /**
23415      * Destroy this splitbar. 
23416      * @param {Boolean} removeEl True to remove the element
23417      */
23418     destroy : function(removeEl){
23419         if(this.shim){
23420             this.shim.remove();
23421         }
23422         this.dd.unreg();
23423         this.proxy.parentNode.removeChild(this.proxy);
23424         if(removeEl){
23425             this.el.remove();
23426         }
23427     }
23428 });
23429
23430 /**
23431  * @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.
23432  */
23433 Roo.SplitBar.createProxy = function(dir){
23434     var proxy = new Roo.Element(document.createElement("div"));
23435     proxy.unselectable();
23436     var cls = 'x-splitbar-proxy';
23437     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
23438     document.body.appendChild(proxy.dom);
23439     return proxy.dom;
23440 };
23441
23442 /** 
23443  * @class Roo.SplitBar.BasicLayoutAdapter
23444  * Default Adapter. It assumes the splitter and resizing element are not positioned
23445  * elements and only gets/sets the width of the element. Generally used for table based layouts.
23446  */
23447 Roo.SplitBar.BasicLayoutAdapter = function(){
23448 };
23449
23450 Roo.SplitBar.BasicLayoutAdapter.prototype = {
23451     // do nothing for now
23452     init : function(s){
23453     
23454     },
23455     /**
23456      * Called before drag operations to get the current size of the resizing element. 
23457      * @param {Roo.SplitBar} s The SplitBar using this adapter
23458      */
23459      getElementSize : function(s){
23460         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23461             return s.resizingEl.getWidth();
23462         }else{
23463             return s.resizingEl.getHeight();
23464         }
23465     },
23466     
23467     /**
23468      * Called after drag operations to set the size of the resizing element.
23469      * @param {Roo.SplitBar} s The SplitBar using this adapter
23470      * @param {Number} newSize The new size to set
23471      * @param {Function} onComplete A function to be invoked when resizing is complete
23472      */
23473     setElementSize : function(s, newSize, onComplete){
23474         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23475             if(!s.animate){
23476                 s.resizingEl.setWidth(newSize);
23477                 if(onComplete){
23478                     onComplete(s, newSize);
23479                 }
23480             }else{
23481                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
23482             }
23483         }else{
23484             
23485             if(!s.animate){
23486                 s.resizingEl.setHeight(newSize);
23487                 if(onComplete){
23488                     onComplete(s, newSize);
23489                 }
23490             }else{
23491                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
23492             }
23493         }
23494     }
23495 };
23496
23497 /** 
23498  *@class Roo.SplitBar.AbsoluteLayoutAdapter
23499  * @extends Roo.SplitBar.BasicLayoutAdapter
23500  * Adapter that  moves the splitter element to align with the resized sizing element. 
23501  * Used with an absolute positioned SplitBar.
23502  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
23503  * document.body, make sure you assign an id to the body element.
23504  */
23505 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
23506     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
23507     this.container = Roo.get(container);
23508 };
23509
23510 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
23511     init : function(s){
23512         this.basic.init(s);
23513     },
23514     
23515     getElementSize : function(s){
23516         return this.basic.getElementSize(s);
23517     },
23518     
23519     setElementSize : function(s, newSize, onComplete){
23520         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
23521     },
23522     
23523     moveSplitter : function(s){
23524         var yes = Roo.SplitBar;
23525         switch(s.placement){
23526             case yes.LEFT:
23527                 s.el.setX(s.resizingEl.getRight());
23528                 break;
23529             case yes.RIGHT:
23530                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
23531                 break;
23532             case yes.TOP:
23533                 s.el.setY(s.resizingEl.getBottom());
23534                 break;
23535             case yes.BOTTOM:
23536                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
23537                 break;
23538         }
23539     }
23540 };
23541
23542 /**
23543  * Orientation constant - Create a vertical SplitBar
23544  * @static
23545  * @type Number
23546  */
23547 Roo.SplitBar.VERTICAL = 1;
23548
23549 /**
23550  * Orientation constant - Create a horizontal SplitBar
23551  * @static
23552  * @type Number
23553  */
23554 Roo.SplitBar.HORIZONTAL = 2;
23555
23556 /**
23557  * Placement constant - The resizing element is to the left of the splitter element
23558  * @static
23559  * @type Number
23560  */
23561 Roo.SplitBar.LEFT = 1;
23562
23563 /**
23564  * Placement constant - The resizing element is to the right of the splitter element
23565  * @static
23566  * @type Number
23567  */
23568 Roo.SplitBar.RIGHT = 2;
23569
23570 /**
23571  * Placement constant - The resizing element is positioned above the splitter element
23572  * @static
23573  * @type Number
23574  */
23575 Roo.SplitBar.TOP = 3;
23576
23577 /**
23578  * Placement constant - The resizing element is positioned under splitter element
23579  * @static
23580  * @type Number
23581  */
23582 Roo.SplitBar.BOTTOM = 4;
23583 /*
23584  * Based on:
23585  * Ext JS Library 1.1.1
23586  * Copyright(c) 2006-2007, Ext JS, LLC.
23587  *
23588  * Originally Released Under LGPL - original licence link has changed is not relivant.
23589  *
23590  * Fork - LGPL
23591  * <script type="text/javascript">
23592  */
23593
23594 /**
23595  * @class Roo.View
23596  * @extends Roo.util.Observable
23597  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
23598  * This class also supports single and multi selection modes. <br>
23599  * Create a data model bound view:
23600  <pre><code>
23601  var store = new Roo.data.Store(...);
23602
23603  var view = new Roo.View({
23604     el : "my-element",
23605     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
23606  
23607     singleSelect: true,
23608     selectedClass: "ydataview-selected",
23609     store: store
23610  });
23611
23612  // listen for node click?
23613  view.on("click", function(vw, index, node, e){
23614  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
23615  });
23616
23617  // load XML data
23618  dataModel.load("foobar.xml");
23619  </code></pre>
23620  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
23621  * <br><br>
23622  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
23623  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
23624  * 
23625  * Note: old style constructor is still suported (container, template, config)
23626  * 
23627  * @constructor
23628  * Create a new View
23629  * @param {Object} config The config object
23630  * 
23631  */
23632 Roo.View = function(config, depreciated_tpl, depreciated_config){
23633     
23634     if (typeof(depreciated_tpl) == 'undefined') {
23635         // new way.. - universal constructor.
23636         Roo.apply(this, config);
23637         this.el  = Roo.get(this.el);
23638     } else {
23639         // old format..
23640         this.el  = Roo.get(config);
23641         this.tpl = depreciated_tpl;
23642         Roo.apply(this, depreciated_config);
23643     }
23644      
23645     
23646     if(typeof(this.tpl) == "string"){
23647         this.tpl = new Roo.Template(this.tpl);
23648     } else {
23649         // support xtype ctors..
23650         this.tpl = new Roo.factory(this.tpl, Roo);
23651     }
23652     
23653     
23654     this.tpl.compile();
23655    
23656
23657      
23658     /** @private */
23659     this.addEvents({
23660         /**
23661          * @event beforeclick
23662          * Fires before a click is processed. Returns false to cancel the default action.
23663          * @param {Roo.View} this
23664          * @param {Number} index The index of the target node
23665          * @param {HTMLElement} node The target node
23666          * @param {Roo.EventObject} e The raw event object
23667          */
23668             "beforeclick" : true,
23669         /**
23670          * @event click
23671          * Fires when a template node is clicked.
23672          * @param {Roo.View} this
23673          * @param {Number} index The index of the target node
23674          * @param {HTMLElement} node The target node
23675          * @param {Roo.EventObject} e The raw event object
23676          */
23677             "click" : true,
23678         /**
23679          * @event dblclick
23680          * Fires when a template node is double clicked.
23681          * @param {Roo.View} this
23682          * @param {Number} index The index of the target node
23683          * @param {HTMLElement} node The target node
23684          * @param {Roo.EventObject} e The raw event object
23685          */
23686             "dblclick" : true,
23687         /**
23688          * @event contextmenu
23689          * Fires when a template node is right clicked.
23690          * @param {Roo.View} this
23691          * @param {Number} index The index of the target node
23692          * @param {HTMLElement} node The target node
23693          * @param {Roo.EventObject} e The raw event object
23694          */
23695             "contextmenu" : true,
23696         /**
23697          * @event selectionchange
23698          * Fires when the selected nodes change.
23699          * @param {Roo.View} this
23700          * @param {Array} selections Array of the selected nodes
23701          */
23702             "selectionchange" : true,
23703     
23704         /**
23705          * @event beforeselect
23706          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
23707          * @param {Roo.View} this
23708          * @param {HTMLElement} node The node to be selected
23709          * @param {Array} selections Array of currently selected nodes
23710          */
23711             "beforeselect" : true,
23712         /**
23713          * @event preparedata
23714          * Fires on every row to render, to allow you to change the data.
23715          * @param {Roo.View} this
23716          * @param {Object} data to be rendered (change this)
23717          */
23718           "preparedata" : true
23719         });
23720
23721     this.el.on({
23722         "click": this.onClick,
23723         "dblclick": this.onDblClick,
23724         "contextmenu": this.onContextMenu,
23725         scope:this
23726     });
23727
23728     this.selections = [];
23729     this.nodes = [];
23730     this.cmp = new Roo.CompositeElementLite([]);
23731     if(this.store){
23732         this.store = Roo.factory(this.store, Roo.data);
23733         this.setStore(this.store, true);
23734     }
23735     Roo.View.superclass.constructor.call(this);
23736 };
23737
23738 Roo.extend(Roo.View, Roo.util.Observable, {
23739     
23740      /**
23741      * @cfg {Roo.data.Store} store Data store to load data from.
23742      */
23743     store : false,
23744     
23745     /**
23746      * @cfg {String|Roo.Element} el The container element.
23747      */
23748     el : '',
23749     
23750     /**
23751      * @cfg {String|Roo.Template} tpl The template used by this View 
23752      */
23753     tpl : false,
23754     
23755     /**
23756      * @cfg {String} selectedClass The css class to add to selected nodes
23757      */
23758     selectedClass : "x-view-selected",
23759      /**
23760      * @cfg {String} emptyText The empty text to show when nothing is loaded.
23761      */
23762     emptyText : "",
23763     /**
23764      * @cfg {Boolean} multiSelect Allow multiple selection
23765      */
23766     multiSelect : false,
23767     /**
23768      * @cfg {Boolean} singleSelect Allow single selection
23769      */
23770     singleSelect:  false,
23771     
23772     /**
23773      * @cfg {Boolean} toggleSelect - selecting 
23774      */
23775     toggleSelect : false,
23776     
23777     /**
23778      * Returns the element this view is bound to.
23779      * @return {Roo.Element}
23780      */
23781     getEl : function(){
23782         return this.el;
23783     },
23784
23785     /**
23786      * Refreshes the view.
23787      */
23788     refresh : function(){
23789         var t = this.tpl;
23790         this.clearSelections();
23791         this.el.update("");
23792         var html = [];
23793         var records = this.store.getRange();
23794         if(records.length < 1){
23795             this.el.update(this.emptyText);
23796             return;
23797         }
23798         for(var i = 0, len = records.length; i < len; i++){
23799             var data = this.prepareData(records[i].data, i, records[i]);
23800             this.fireEvent("preparedata", this, data, i, records[i]);
23801             html[html.length] = t.apply(data);
23802         }
23803         this.el.update(html.join(""));
23804         this.nodes = this.el.dom.childNodes;
23805         this.updateIndexes(0);
23806     },
23807
23808     /**
23809      * Function to override to reformat the data that is sent to
23810      * the template for each node.
23811      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
23812      * a JSON object for an UpdateManager bound view).
23813      */
23814     prepareData : function(data){
23815         return data;
23816     },
23817
23818     onUpdate : function(ds, record){
23819         this.clearSelections();
23820         var index = this.store.indexOf(record);
23821         var n = this.nodes[index];
23822         this.tpl.insertBefore(n, this.prepareData(record.data));
23823         n.parentNode.removeChild(n);
23824         this.updateIndexes(index, index);
23825     },
23826
23827     onAdd : function(ds, records, index){
23828         this.clearSelections();
23829         if(this.nodes.length == 0){
23830             this.refresh();
23831             return;
23832         }
23833         var n = this.nodes[index];
23834         for(var i = 0, len = records.length; i < len; i++){
23835             var d = this.prepareData(records[i].data);
23836             if(n){
23837                 this.tpl.insertBefore(n, d);
23838             }else{
23839                 this.tpl.append(this.el, d);
23840             }
23841         }
23842         this.updateIndexes(index);
23843     },
23844
23845     onRemove : function(ds, record, index){
23846         this.clearSelections();
23847         this.el.dom.removeChild(this.nodes[index]);
23848         this.updateIndexes(index);
23849     },
23850
23851     /**
23852      * Refresh an individual node.
23853      * @param {Number} index
23854      */
23855     refreshNode : function(index){
23856         this.onUpdate(this.store, this.store.getAt(index));
23857     },
23858
23859     updateIndexes : function(startIndex, endIndex){
23860         var ns = this.nodes;
23861         startIndex = startIndex || 0;
23862         endIndex = endIndex || ns.length - 1;
23863         for(var i = startIndex; i <= endIndex; i++){
23864             ns[i].nodeIndex = i;
23865         }
23866     },
23867
23868     /**
23869      * Changes the data store this view uses and refresh the view.
23870      * @param {Store} store
23871      */
23872     setStore : function(store, initial){
23873         if(!initial && this.store){
23874             this.store.un("datachanged", this.refresh);
23875             this.store.un("add", this.onAdd);
23876             this.store.un("remove", this.onRemove);
23877             this.store.un("update", this.onUpdate);
23878             this.store.un("clear", this.refresh);
23879         }
23880         if(store){
23881           
23882             store.on("datachanged", this.refresh, this);
23883             store.on("add", this.onAdd, this);
23884             store.on("remove", this.onRemove, this);
23885             store.on("update", this.onUpdate, this);
23886             store.on("clear", this.refresh, this);
23887         }
23888         
23889         if(store){
23890             this.refresh();
23891         }
23892     },
23893
23894     /**
23895      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
23896      * @param {HTMLElement} node
23897      * @return {HTMLElement} The template node
23898      */
23899     findItemFromChild : function(node){
23900         var el = this.el.dom;
23901         if(!node || node.parentNode == el){
23902                     return node;
23903             }
23904             var p = node.parentNode;
23905             while(p && p != el){
23906             if(p.parentNode == el){
23907                 return p;
23908             }
23909             p = p.parentNode;
23910         }
23911             return null;
23912     },
23913
23914     /** @ignore */
23915     onClick : function(e){
23916         var item = this.findItemFromChild(e.getTarget());
23917         if(item){
23918             var index = this.indexOf(item);
23919             if(this.onItemClick(item, index, e) !== false){
23920                 this.fireEvent("click", this, index, item, e);
23921             }
23922         }else{
23923             this.clearSelections();
23924         }
23925     },
23926
23927     /** @ignore */
23928     onContextMenu : function(e){
23929         var item = this.findItemFromChild(e.getTarget());
23930         if(item){
23931             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
23932         }
23933     },
23934
23935     /** @ignore */
23936     onDblClick : function(e){
23937         var item = this.findItemFromChild(e.getTarget());
23938         if(item){
23939             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
23940         }
23941     },
23942
23943     onItemClick : function(item, index, e)
23944     {
23945         if(this.fireEvent("beforeclick", this, index, item, e) === false){
23946             return false;
23947         }
23948         if (this.toggleSelect) {
23949             var m = this.isSelected(item) ? 'unselect' : 'select';
23950             Roo.log(m);
23951             var _t = this;
23952             _t[m](item, true, false);
23953             return true;
23954         }
23955         if(this.multiSelect || this.singleSelect){
23956             if(this.multiSelect && e.shiftKey && this.lastSelection){
23957                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
23958             }else{
23959                 this.select(item, this.multiSelect && e.ctrlKey);
23960                 this.lastSelection = item;
23961             }
23962             e.preventDefault();
23963         }
23964         return true;
23965     },
23966
23967     /**
23968      * Get the number of selected nodes.
23969      * @return {Number}
23970      */
23971     getSelectionCount : function(){
23972         return this.selections.length;
23973     },
23974
23975     /**
23976      * Get the currently selected nodes.
23977      * @return {Array} An array of HTMLElements
23978      */
23979     getSelectedNodes : function(){
23980         return this.selections;
23981     },
23982
23983     /**
23984      * Get the indexes of the selected nodes.
23985      * @return {Array}
23986      */
23987     getSelectedIndexes : function(){
23988         var indexes = [], s = this.selections;
23989         for(var i = 0, len = s.length; i < len; i++){
23990             indexes.push(s[i].nodeIndex);
23991         }
23992         return indexes;
23993     },
23994
23995     /**
23996      * Clear all selections
23997      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
23998      */
23999     clearSelections : function(suppressEvent){
24000         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
24001             this.cmp.elements = this.selections;
24002             this.cmp.removeClass(this.selectedClass);
24003             this.selections = [];
24004             if(!suppressEvent){
24005                 this.fireEvent("selectionchange", this, this.selections);
24006             }
24007         }
24008     },
24009
24010     /**
24011      * Returns true if the passed node is selected
24012      * @param {HTMLElement/Number} node The node or node index
24013      * @return {Boolean}
24014      */
24015     isSelected : function(node){
24016         var s = this.selections;
24017         if(s.length < 1){
24018             return false;
24019         }
24020         node = this.getNode(node);
24021         return s.indexOf(node) !== -1;
24022     },
24023
24024     /**
24025      * Selects nodes.
24026      * @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
24027      * @param {Boolean} keepExisting (optional) true to keep existing selections
24028      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
24029      */
24030     select : function(nodeInfo, keepExisting, suppressEvent){
24031         if(nodeInfo instanceof Array){
24032             if(!keepExisting){
24033                 this.clearSelections(true);
24034             }
24035             for(var i = 0, len = nodeInfo.length; i < len; i++){
24036                 this.select(nodeInfo[i], true, true);
24037             }
24038             return;
24039         } 
24040         var node = this.getNode(nodeInfo);
24041         if(!node || this.isSelected(node)){
24042             return; // already selected.
24043         }
24044         if(!keepExisting){
24045             this.clearSelections(true);
24046         }
24047         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
24048             Roo.fly(node).addClass(this.selectedClass);
24049             this.selections.push(node);
24050             if(!suppressEvent){
24051                 this.fireEvent("selectionchange", this, this.selections);
24052             }
24053         }
24054         
24055         
24056     },
24057       /**
24058      * Unselects nodes.
24059      * @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
24060      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
24061      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
24062      */
24063     unselect : function(nodeInfo, keepExisting, suppressEvent)
24064     {
24065         if(nodeInfo instanceof Array){
24066             Roo.each(this.selections, function(s) {
24067                 this.unselect(s, nodeInfo);
24068             }, this);
24069             return;
24070         }
24071         var node = this.getNode(nodeInfo);
24072         if(!node || !this.isSelected(node)){
24073             Roo.log("not selected");
24074             return; // not selected.
24075         }
24076         // fireevent???
24077         var ns = [];
24078         Roo.each(this.selections, function(s) {
24079             if (s == node ) {
24080                 Roo.fly(node).removeClass(this.selectedClass);
24081
24082                 return;
24083             }
24084             ns.push(s);
24085         },this);
24086         
24087         this.selections= ns;
24088         this.fireEvent("selectionchange", this, this.selections);
24089     },
24090
24091     /**
24092      * Gets a template node.
24093      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
24094      * @return {HTMLElement} The node or null if it wasn't found
24095      */
24096     getNode : function(nodeInfo){
24097         if(typeof nodeInfo == "string"){
24098             return document.getElementById(nodeInfo);
24099         }else if(typeof nodeInfo == "number"){
24100             return this.nodes[nodeInfo];
24101         }
24102         return nodeInfo;
24103     },
24104
24105     /**
24106      * Gets a range template nodes.
24107      * @param {Number} startIndex
24108      * @param {Number} endIndex
24109      * @return {Array} An array of nodes
24110      */
24111     getNodes : function(start, end){
24112         var ns = this.nodes;
24113         start = start || 0;
24114         end = typeof end == "undefined" ? ns.length - 1 : end;
24115         var nodes = [];
24116         if(start <= end){
24117             for(var i = start; i <= end; i++){
24118                 nodes.push(ns[i]);
24119             }
24120         } else{
24121             for(var i = start; i >= end; i--){
24122                 nodes.push(ns[i]);
24123             }
24124         }
24125         return nodes;
24126     },
24127
24128     /**
24129      * Finds the index of the passed node
24130      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
24131      * @return {Number} The index of the node or -1
24132      */
24133     indexOf : function(node){
24134         node = this.getNode(node);
24135         if(typeof node.nodeIndex == "number"){
24136             return node.nodeIndex;
24137         }
24138         var ns = this.nodes;
24139         for(var i = 0, len = ns.length; i < len; i++){
24140             if(ns[i] == node){
24141                 return i;
24142             }
24143         }
24144         return -1;
24145     }
24146 });
24147 /*
24148  * Based on:
24149  * Ext JS Library 1.1.1
24150  * Copyright(c) 2006-2007, Ext JS, LLC.
24151  *
24152  * Originally Released Under LGPL - original licence link has changed is not relivant.
24153  *
24154  * Fork - LGPL
24155  * <script type="text/javascript">
24156  */
24157
24158 /**
24159  * @class Roo.JsonView
24160  * @extends Roo.View
24161  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
24162 <pre><code>
24163 var view = new Roo.JsonView({
24164     container: "my-element",
24165     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
24166     multiSelect: true, 
24167     jsonRoot: "data" 
24168 });
24169
24170 // listen for node click?
24171 view.on("click", function(vw, index, node, e){
24172     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
24173 });
24174
24175 // direct load of JSON data
24176 view.load("foobar.php");
24177
24178 // Example from my blog list
24179 var tpl = new Roo.Template(
24180     '&lt;div class="entry"&gt;' +
24181     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
24182     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
24183     "&lt;/div&gt;&lt;hr /&gt;"
24184 );
24185
24186 var moreView = new Roo.JsonView({
24187     container :  "entry-list", 
24188     template : tpl,
24189     jsonRoot: "posts"
24190 });
24191 moreView.on("beforerender", this.sortEntries, this);
24192 moreView.load({
24193     url: "/blog/get-posts.php",
24194     params: "allposts=true",
24195     text: "Loading Blog Entries..."
24196 });
24197 </code></pre>
24198
24199 * Note: old code is supported with arguments : (container, template, config)
24200
24201
24202  * @constructor
24203  * Create a new JsonView
24204  * 
24205  * @param {Object} config The config object
24206  * 
24207  */
24208 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
24209     
24210     
24211     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
24212
24213     var um = this.el.getUpdateManager();
24214     um.setRenderer(this);
24215     um.on("update", this.onLoad, this);
24216     um.on("failure", this.onLoadException, this);
24217
24218     /**
24219      * @event beforerender
24220      * Fires before rendering of the downloaded JSON data.
24221      * @param {Roo.JsonView} this
24222      * @param {Object} data The JSON data loaded
24223      */
24224     /**
24225      * @event load
24226      * Fires when data is loaded.
24227      * @param {Roo.JsonView} this
24228      * @param {Object} data The JSON data loaded
24229      * @param {Object} response The raw Connect response object
24230      */
24231     /**
24232      * @event loadexception
24233      * Fires when loading fails.
24234      * @param {Roo.JsonView} this
24235      * @param {Object} response The raw Connect response object
24236      */
24237     this.addEvents({
24238         'beforerender' : true,
24239         'load' : true,
24240         'loadexception' : true
24241     });
24242 };
24243 Roo.extend(Roo.JsonView, Roo.View, {
24244     /**
24245      * @type {String} The root property in the loaded JSON object that contains the data
24246      */
24247     jsonRoot : "",
24248
24249     /**
24250      * Refreshes the view.
24251      */
24252     refresh : function(){
24253         this.clearSelections();
24254         this.el.update("");
24255         var html = [];
24256         var o = this.jsonData;
24257         if(o && o.length > 0){
24258             for(var i = 0, len = o.length; i < len; i++){
24259                 var data = this.prepareData(o[i], i, o);
24260                 html[html.length] = this.tpl.apply(data);
24261             }
24262         }else{
24263             html.push(this.emptyText);
24264         }
24265         this.el.update(html.join(""));
24266         this.nodes = this.el.dom.childNodes;
24267         this.updateIndexes(0);
24268     },
24269
24270     /**
24271      * 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.
24272      * @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:
24273      <pre><code>
24274      view.load({
24275          url: "your-url.php",
24276          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
24277          callback: yourFunction,
24278          scope: yourObject, //(optional scope)
24279          discardUrl: false,
24280          nocache: false,
24281          text: "Loading...",
24282          timeout: 30,
24283          scripts: false
24284      });
24285      </code></pre>
24286      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
24287      * 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.
24288      * @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}
24289      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
24290      * @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.
24291      */
24292     load : function(){
24293         var um = this.el.getUpdateManager();
24294         um.update.apply(um, arguments);
24295     },
24296
24297     render : function(el, response){
24298         this.clearSelections();
24299         this.el.update("");
24300         var o;
24301         try{
24302             o = Roo.util.JSON.decode(response.responseText);
24303             if(this.jsonRoot){
24304                 
24305                 o = o[this.jsonRoot];
24306             }
24307         } catch(e){
24308         }
24309         /**
24310          * The current JSON data or null
24311          */
24312         this.jsonData = o;
24313         this.beforeRender();
24314         this.refresh();
24315     },
24316
24317 /**
24318  * Get the number of records in the current JSON dataset
24319  * @return {Number}
24320  */
24321     getCount : function(){
24322         return this.jsonData ? this.jsonData.length : 0;
24323     },
24324
24325 /**
24326  * Returns the JSON object for the specified node(s)
24327  * @param {HTMLElement/Array} node The node or an array of nodes
24328  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
24329  * you get the JSON object for the node
24330  */
24331     getNodeData : function(node){
24332         if(node instanceof Array){
24333             var data = [];
24334             for(var i = 0, len = node.length; i < len; i++){
24335                 data.push(this.getNodeData(node[i]));
24336             }
24337             return data;
24338         }
24339         return this.jsonData[this.indexOf(node)] || null;
24340     },
24341
24342     beforeRender : function(){
24343         this.snapshot = this.jsonData;
24344         if(this.sortInfo){
24345             this.sort.apply(this, this.sortInfo);
24346         }
24347         this.fireEvent("beforerender", this, this.jsonData);
24348     },
24349
24350     onLoad : function(el, o){
24351         this.fireEvent("load", this, this.jsonData, o);
24352     },
24353
24354     onLoadException : function(el, o){
24355         this.fireEvent("loadexception", this, o);
24356     },
24357
24358 /**
24359  * Filter the data by a specific property.
24360  * @param {String} property A property on your JSON objects
24361  * @param {String/RegExp} value Either string that the property values
24362  * should start with, or a RegExp to test against the property
24363  */
24364     filter : function(property, value){
24365         if(this.jsonData){
24366             var data = [];
24367             var ss = this.snapshot;
24368             if(typeof value == "string"){
24369                 var vlen = value.length;
24370                 if(vlen == 0){
24371                     this.clearFilter();
24372                     return;
24373                 }
24374                 value = value.toLowerCase();
24375                 for(var i = 0, len = ss.length; i < len; i++){
24376                     var o = ss[i];
24377                     if(o[property].substr(0, vlen).toLowerCase() == value){
24378                         data.push(o);
24379                     }
24380                 }
24381             } else if(value.exec){ // regex?
24382                 for(var i = 0, len = ss.length; i < len; i++){
24383                     var o = ss[i];
24384                     if(value.test(o[property])){
24385                         data.push(o);
24386                     }
24387                 }
24388             } else{
24389                 return;
24390             }
24391             this.jsonData = data;
24392             this.refresh();
24393         }
24394     },
24395
24396 /**
24397  * Filter by a function. The passed function will be called with each
24398  * object in the current dataset. If the function returns true the value is kept,
24399  * otherwise it is filtered.
24400  * @param {Function} fn
24401  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
24402  */
24403     filterBy : function(fn, scope){
24404         if(this.jsonData){
24405             var data = [];
24406             var ss = this.snapshot;
24407             for(var i = 0, len = ss.length; i < len; i++){
24408                 var o = ss[i];
24409                 if(fn.call(scope || this, o)){
24410                     data.push(o);
24411                 }
24412             }
24413             this.jsonData = data;
24414             this.refresh();
24415         }
24416     },
24417
24418 /**
24419  * Clears the current filter.
24420  */
24421     clearFilter : function(){
24422         if(this.snapshot && this.jsonData != this.snapshot){
24423             this.jsonData = this.snapshot;
24424             this.refresh();
24425         }
24426     },
24427
24428
24429 /**
24430  * Sorts the data for this view and refreshes it.
24431  * @param {String} property A property on your JSON objects to sort on
24432  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
24433  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
24434  */
24435     sort : function(property, dir, sortType){
24436         this.sortInfo = Array.prototype.slice.call(arguments, 0);
24437         if(this.jsonData){
24438             var p = property;
24439             var dsc = dir && dir.toLowerCase() == "desc";
24440             var f = function(o1, o2){
24441                 var v1 = sortType ? sortType(o1[p]) : o1[p];
24442                 var v2 = sortType ? sortType(o2[p]) : o2[p];
24443                 ;
24444                 if(v1 < v2){
24445                     return dsc ? +1 : -1;
24446                 } else if(v1 > v2){
24447                     return dsc ? -1 : +1;
24448                 } else{
24449                     return 0;
24450                 }
24451             };
24452             this.jsonData.sort(f);
24453             this.refresh();
24454             if(this.jsonData != this.snapshot){
24455                 this.snapshot.sort(f);
24456             }
24457         }
24458     }
24459 });/*
24460  * Based on:
24461  * Ext JS Library 1.1.1
24462  * Copyright(c) 2006-2007, Ext JS, LLC.
24463  *
24464  * Originally Released Under LGPL - original licence link has changed is not relivant.
24465  *
24466  * Fork - LGPL
24467  * <script type="text/javascript">
24468  */
24469  
24470
24471 /**
24472  * @class Roo.ColorPalette
24473  * @extends Roo.Component
24474  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
24475  * Here's an example of typical usage:
24476  * <pre><code>
24477 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
24478 cp.render('my-div');
24479
24480 cp.on('select', function(palette, selColor){
24481     // do something with selColor
24482 });
24483 </code></pre>
24484  * @constructor
24485  * Create a new ColorPalette
24486  * @param {Object} config The config object
24487  */
24488 Roo.ColorPalette = function(config){
24489     Roo.ColorPalette.superclass.constructor.call(this, config);
24490     this.addEvents({
24491         /**
24492              * @event select
24493              * Fires when a color is selected
24494              * @param {ColorPalette} this
24495              * @param {String} color The 6-digit color hex code (without the # symbol)
24496              */
24497         select: true
24498     });
24499
24500     if(this.handler){
24501         this.on("select", this.handler, this.scope, true);
24502     }
24503 };
24504 Roo.extend(Roo.ColorPalette, Roo.Component, {
24505     /**
24506      * @cfg {String} itemCls
24507      * The CSS class to apply to the containing element (defaults to "x-color-palette")
24508      */
24509     itemCls : "x-color-palette",
24510     /**
24511      * @cfg {String} value
24512      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
24513      * the hex codes are case-sensitive.
24514      */
24515     value : null,
24516     clickEvent:'click',
24517     // private
24518     ctype: "Roo.ColorPalette",
24519
24520     /**
24521      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
24522      */
24523     allowReselect : false,
24524
24525     /**
24526      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
24527      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
24528      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
24529      * of colors with the width setting until the box is symmetrical.</p>
24530      * <p>You can override individual colors if needed:</p>
24531      * <pre><code>
24532 var cp = new Roo.ColorPalette();
24533 cp.colors[0] = "FF0000";  // change the first box to red
24534 </code></pre>
24535
24536 Or you can provide a custom array of your own for complete control:
24537 <pre><code>
24538 var cp = new Roo.ColorPalette();
24539 cp.colors = ["000000", "993300", "333300"];
24540 </code></pre>
24541      * @type Array
24542      */
24543     colors : [
24544         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
24545         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
24546         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
24547         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
24548         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
24549     ],
24550
24551     // private
24552     onRender : function(container, position){
24553         var t = new Roo.MasterTemplate(
24554             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
24555         );
24556         var c = this.colors;
24557         for(var i = 0, len = c.length; i < len; i++){
24558             t.add([c[i]]);
24559         }
24560         var el = document.createElement("div");
24561         el.className = this.itemCls;
24562         t.overwrite(el);
24563         container.dom.insertBefore(el, position);
24564         this.el = Roo.get(el);
24565         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
24566         if(this.clickEvent != 'click'){
24567             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
24568         }
24569     },
24570
24571     // private
24572     afterRender : function(){
24573         Roo.ColorPalette.superclass.afterRender.call(this);
24574         if(this.value){
24575             var s = this.value;
24576             this.value = null;
24577             this.select(s);
24578         }
24579     },
24580
24581     // private
24582     handleClick : function(e, t){
24583         e.preventDefault();
24584         if(!this.disabled){
24585             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
24586             this.select(c.toUpperCase());
24587         }
24588     },
24589
24590     /**
24591      * Selects the specified color in the palette (fires the select event)
24592      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
24593      */
24594     select : function(color){
24595         color = color.replace("#", "");
24596         if(color != this.value || this.allowReselect){
24597             var el = this.el;
24598             if(this.value){
24599                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
24600             }
24601             el.child("a.color-"+color).addClass("x-color-palette-sel");
24602             this.value = color;
24603             this.fireEvent("select", this, color);
24604         }
24605     }
24606 });/*
24607  * Based on:
24608  * Ext JS Library 1.1.1
24609  * Copyright(c) 2006-2007, Ext JS, LLC.
24610  *
24611  * Originally Released Under LGPL - original licence link has changed is not relivant.
24612  *
24613  * Fork - LGPL
24614  * <script type="text/javascript">
24615  */
24616  
24617 /**
24618  * @class Roo.DatePicker
24619  * @extends Roo.Component
24620  * Simple date picker class.
24621  * @constructor
24622  * Create a new DatePicker
24623  * @param {Object} config The config object
24624  */
24625 Roo.DatePicker = function(config){
24626     Roo.DatePicker.superclass.constructor.call(this, config);
24627
24628     this.value = config && config.value ?
24629                  config.value.clearTime() : new Date().clearTime();
24630
24631     this.addEvents({
24632         /**
24633              * @event select
24634              * Fires when a date is selected
24635              * @param {DatePicker} this
24636              * @param {Date} date The selected date
24637              */
24638         'select': true,
24639         /**
24640              * @event monthchange
24641              * Fires when the displayed month changes 
24642              * @param {DatePicker} this
24643              * @param {Date} date The selected month
24644              */
24645         'monthchange': true
24646     });
24647
24648     if(this.handler){
24649         this.on("select", this.handler,  this.scope || this);
24650     }
24651     // build the disabledDatesRE
24652     if(!this.disabledDatesRE && this.disabledDates){
24653         var dd = this.disabledDates;
24654         var re = "(?:";
24655         for(var i = 0; i < dd.length; i++){
24656             re += dd[i];
24657             if(i != dd.length-1) re += "|";
24658         }
24659         this.disabledDatesRE = new RegExp(re + ")");
24660     }
24661 };
24662
24663 Roo.extend(Roo.DatePicker, Roo.Component, {
24664     /**
24665      * @cfg {String} todayText
24666      * The text to display on the button that selects the current date (defaults to "Today")
24667      */
24668     todayText : "Today",
24669     /**
24670      * @cfg {String} okText
24671      * The text to display on the ok button
24672      */
24673     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
24674     /**
24675      * @cfg {String} cancelText
24676      * The text to display on the cancel button
24677      */
24678     cancelText : "Cancel",
24679     /**
24680      * @cfg {String} todayTip
24681      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
24682      */
24683     todayTip : "{0} (Spacebar)",
24684     /**
24685      * @cfg {Date} minDate
24686      * Minimum allowable date (JavaScript date object, defaults to null)
24687      */
24688     minDate : null,
24689     /**
24690      * @cfg {Date} maxDate
24691      * Maximum allowable date (JavaScript date object, defaults to null)
24692      */
24693     maxDate : null,
24694     /**
24695      * @cfg {String} minText
24696      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
24697      */
24698     minText : "This date is before the minimum date",
24699     /**
24700      * @cfg {String} maxText
24701      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
24702      */
24703     maxText : "This date is after the maximum date",
24704     /**
24705      * @cfg {String} format
24706      * The default date format string which can be overriden for localization support.  The format must be
24707      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
24708      */
24709     format : "m/d/y",
24710     /**
24711      * @cfg {Array} disabledDays
24712      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
24713      */
24714     disabledDays : null,
24715     /**
24716      * @cfg {String} disabledDaysText
24717      * The tooltip to display when the date falls on a disabled day (defaults to "")
24718      */
24719     disabledDaysText : "",
24720     /**
24721      * @cfg {RegExp} disabledDatesRE
24722      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
24723      */
24724     disabledDatesRE : null,
24725     /**
24726      * @cfg {String} disabledDatesText
24727      * The tooltip text to display when the date falls on a disabled date (defaults to "")
24728      */
24729     disabledDatesText : "",
24730     /**
24731      * @cfg {Boolean} constrainToViewport
24732      * True to constrain the date picker to the viewport (defaults to true)
24733      */
24734     constrainToViewport : true,
24735     /**
24736      * @cfg {Array} monthNames
24737      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
24738      */
24739     monthNames : Date.monthNames,
24740     /**
24741      * @cfg {Array} dayNames
24742      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
24743      */
24744     dayNames : Date.dayNames,
24745     /**
24746      * @cfg {String} nextText
24747      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
24748      */
24749     nextText: 'Next Month (Control+Right)',
24750     /**
24751      * @cfg {String} prevText
24752      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
24753      */
24754     prevText: 'Previous Month (Control+Left)',
24755     /**
24756      * @cfg {String} monthYearText
24757      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
24758      */
24759     monthYearText: 'Choose a month (Control+Up/Down to move years)',
24760     /**
24761      * @cfg {Number} startDay
24762      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
24763      */
24764     startDay : 0,
24765     /**
24766      * @cfg {Bool} showClear
24767      * Show a clear button (usefull for date form elements that can be blank.)
24768      */
24769     
24770     showClear: false,
24771     
24772     /**
24773      * Sets the value of the date field
24774      * @param {Date} value The date to set
24775      */
24776     setValue : function(value){
24777         var old = this.value;
24778         this.value = value.clearTime(true);
24779         if(this.el){
24780             this.update(this.value);
24781         }
24782     },
24783
24784     /**
24785      * Gets the current selected value of the date field
24786      * @return {Date} The selected date
24787      */
24788     getValue : function(){
24789         return this.value;
24790     },
24791
24792     // private
24793     focus : function(){
24794         if(this.el){
24795             this.update(this.activeDate);
24796         }
24797     },
24798
24799     // private
24800     onRender : function(container, position){
24801         var m = [
24802              '<table cellspacing="0">',
24803                 '<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>',
24804                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
24805         var dn = this.dayNames;
24806         for(var i = 0; i < 7; i++){
24807             var d = this.startDay+i;
24808             if(d > 6){
24809                 d = d-7;
24810             }
24811             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
24812         }
24813         m[m.length] = "</tr></thead><tbody><tr>";
24814         for(var i = 0; i < 42; i++) {
24815             if(i % 7 == 0 && i != 0){
24816                 m[m.length] = "</tr><tr>";
24817             }
24818             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
24819         }
24820         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
24821             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
24822
24823         var el = document.createElement("div");
24824         el.className = "x-date-picker";
24825         el.innerHTML = m.join("");
24826
24827         container.dom.insertBefore(el, position);
24828
24829         this.el = Roo.get(el);
24830         this.eventEl = Roo.get(el.firstChild);
24831
24832         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
24833             handler: this.showPrevMonth,
24834             scope: this,
24835             preventDefault:true,
24836             stopDefault:true
24837         });
24838
24839         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
24840             handler: this.showNextMonth,
24841             scope: this,
24842             preventDefault:true,
24843             stopDefault:true
24844         });
24845
24846         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
24847
24848         this.monthPicker = this.el.down('div.x-date-mp');
24849         this.monthPicker.enableDisplayMode('block');
24850         
24851         var kn = new Roo.KeyNav(this.eventEl, {
24852             "left" : function(e){
24853                 e.ctrlKey ?
24854                     this.showPrevMonth() :
24855                     this.update(this.activeDate.add("d", -1));
24856             },
24857
24858             "right" : function(e){
24859                 e.ctrlKey ?
24860                     this.showNextMonth() :
24861                     this.update(this.activeDate.add("d", 1));
24862             },
24863
24864             "up" : function(e){
24865                 e.ctrlKey ?
24866                     this.showNextYear() :
24867                     this.update(this.activeDate.add("d", -7));
24868             },
24869
24870             "down" : function(e){
24871                 e.ctrlKey ?
24872                     this.showPrevYear() :
24873                     this.update(this.activeDate.add("d", 7));
24874             },
24875
24876             "pageUp" : function(e){
24877                 this.showNextMonth();
24878             },
24879
24880             "pageDown" : function(e){
24881                 this.showPrevMonth();
24882             },
24883
24884             "enter" : function(e){
24885                 e.stopPropagation();
24886                 return true;
24887             },
24888
24889             scope : this
24890         });
24891
24892         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
24893
24894         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
24895
24896         this.el.unselectable();
24897         
24898         this.cells = this.el.select("table.x-date-inner tbody td");
24899         this.textNodes = this.el.query("table.x-date-inner tbody span");
24900
24901         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
24902             text: "&#160;",
24903             tooltip: this.monthYearText
24904         });
24905
24906         this.mbtn.on('click', this.showMonthPicker, this);
24907         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
24908
24909
24910         var today = (new Date()).dateFormat(this.format);
24911         
24912         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
24913         if (this.showClear) {
24914             baseTb.add( new Roo.Toolbar.Fill());
24915         }
24916         baseTb.add({
24917             text: String.format(this.todayText, today),
24918             tooltip: String.format(this.todayTip, today),
24919             handler: this.selectToday,
24920             scope: this
24921         });
24922         
24923         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
24924             
24925         //});
24926         if (this.showClear) {
24927             
24928             baseTb.add( new Roo.Toolbar.Fill());
24929             baseTb.add({
24930                 text: '&#160;',
24931                 cls: 'x-btn-icon x-btn-clear',
24932                 handler: function() {
24933                     //this.value = '';
24934                     this.fireEvent("select", this, '');
24935                 },
24936                 scope: this
24937             });
24938         }
24939         
24940         
24941         if(Roo.isIE){
24942             this.el.repaint();
24943         }
24944         this.update(this.value);
24945     },
24946
24947     createMonthPicker : function(){
24948         if(!this.monthPicker.dom.firstChild){
24949             var buf = ['<table border="0" cellspacing="0">'];
24950             for(var i = 0; i < 6; i++){
24951                 buf.push(
24952                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
24953                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
24954                     i == 0 ?
24955                     '<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>' :
24956                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
24957                 );
24958             }
24959             buf.push(
24960                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
24961                     this.okText,
24962                     '</button><button type="button" class="x-date-mp-cancel">',
24963                     this.cancelText,
24964                     '</button></td></tr>',
24965                 '</table>'
24966             );
24967             this.monthPicker.update(buf.join(''));
24968             this.monthPicker.on('click', this.onMonthClick, this);
24969             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
24970
24971             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
24972             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
24973
24974             this.mpMonths.each(function(m, a, i){
24975                 i += 1;
24976                 if((i%2) == 0){
24977                     m.dom.xmonth = 5 + Math.round(i * .5);
24978                 }else{
24979                     m.dom.xmonth = Math.round((i-1) * .5);
24980                 }
24981             });
24982         }
24983     },
24984
24985     showMonthPicker : function(){
24986         this.createMonthPicker();
24987         var size = this.el.getSize();
24988         this.monthPicker.setSize(size);
24989         this.monthPicker.child('table').setSize(size);
24990
24991         this.mpSelMonth = (this.activeDate || this.value).getMonth();
24992         this.updateMPMonth(this.mpSelMonth);
24993         this.mpSelYear = (this.activeDate || this.value).getFullYear();
24994         this.updateMPYear(this.mpSelYear);
24995
24996         this.monthPicker.slideIn('t', {duration:.2});
24997     },
24998
24999     updateMPYear : function(y){
25000         this.mpyear = y;
25001         var ys = this.mpYears.elements;
25002         for(var i = 1; i <= 10; i++){
25003             var td = ys[i-1], y2;
25004             if((i%2) == 0){
25005                 y2 = y + Math.round(i * .5);
25006                 td.firstChild.innerHTML = y2;
25007                 td.xyear = y2;
25008             }else{
25009                 y2 = y - (5-Math.round(i * .5));
25010                 td.firstChild.innerHTML = y2;
25011                 td.xyear = y2;
25012             }
25013             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
25014         }
25015     },
25016
25017     updateMPMonth : function(sm){
25018         this.mpMonths.each(function(m, a, i){
25019             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
25020         });
25021     },
25022
25023     selectMPMonth: function(m){
25024         
25025     },
25026
25027     onMonthClick : function(e, t){
25028         e.stopEvent();
25029         var el = new Roo.Element(t), pn;
25030         if(el.is('button.x-date-mp-cancel')){
25031             this.hideMonthPicker();
25032         }
25033         else if(el.is('button.x-date-mp-ok')){
25034             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
25035             this.hideMonthPicker();
25036         }
25037         else if(pn = el.up('td.x-date-mp-month', 2)){
25038             this.mpMonths.removeClass('x-date-mp-sel');
25039             pn.addClass('x-date-mp-sel');
25040             this.mpSelMonth = pn.dom.xmonth;
25041         }
25042         else if(pn = el.up('td.x-date-mp-year', 2)){
25043             this.mpYears.removeClass('x-date-mp-sel');
25044             pn.addClass('x-date-mp-sel');
25045             this.mpSelYear = pn.dom.xyear;
25046         }
25047         else if(el.is('a.x-date-mp-prev')){
25048             this.updateMPYear(this.mpyear-10);
25049         }
25050         else if(el.is('a.x-date-mp-next')){
25051             this.updateMPYear(this.mpyear+10);
25052         }
25053     },
25054
25055     onMonthDblClick : function(e, t){
25056         e.stopEvent();
25057         var el = new Roo.Element(t), pn;
25058         if(pn = el.up('td.x-date-mp-month', 2)){
25059             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
25060             this.hideMonthPicker();
25061         }
25062         else if(pn = el.up('td.x-date-mp-year', 2)){
25063             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
25064             this.hideMonthPicker();
25065         }
25066     },
25067
25068     hideMonthPicker : function(disableAnim){
25069         if(this.monthPicker){
25070             if(disableAnim === true){
25071                 this.monthPicker.hide();
25072             }else{
25073                 this.monthPicker.slideOut('t', {duration:.2});
25074             }
25075         }
25076     },
25077
25078     // private
25079     showPrevMonth : function(e){
25080         this.update(this.activeDate.add("mo", -1));
25081     },
25082
25083     // private
25084     showNextMonth : function(e){
25085         this.update(this.activeDate.add("mo", 1));
25086     },
25087
25088     // private
25089     showPrevYear : function(){
25090         this.update(this.activeDate.add("y", -1));
25091     },
25092
25093     // private
25094     showNextYear : function(){
25095         this.update(this.activeDate.add("y", 1));
25096     },
25097
25098     // private
25099     handleMouseWheel : function(e){
25100         var delta = e.getWheelDelta();
25101         if(delta > 0){
25102             this.showPrevMonth();
25103             e.stopEvent();
25104         } else if(delta < 0){
25105             this.showNextMonth();
25106             e.stopEvent();
25107         }
25108     },
25109
25110     // private
25111     handleDateClick : function(e, t){
25112         e.stopEvent();
25113         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
25114             this.setValue(new Date(t.dateValue));
25115             this.fireEvent("select", this, this.value);
25116         }
25117     },
25118
25119     // private
25120     selectToday : function(){
25121         this.setValue(new Date().clearTime());
25122         this.fireEvent("select", this, this.value);
25123     },
25124
25125     // private
25126     update : function(date)
25127     {
25128         var vd = this.activeDate;
25129         this.activeDate = date;
25130         if(vd && this.el){
25131             var t = date.getTime();
25132             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
25133                 this.cells.removeClass("x-date-selected");
25134                 this.cells.each(function(c){
25135                    if(c.dom.firstChild.dateValue == t){
25136                        c.addClass("x-date-selected");
25137                        setTimeout(function(){
25138                             try{c.dom.firstChild.focus();}catch(e){}
25139                        }, 50);
25140                        return false;
25141                    }
25142                 });
25143                 return;
25144             }
25145         }
25146         
25147         var days = date.getDaysInMonth();
25148         var firstOfMonth = date.getFirstDateOfMonth();
25149         var startingPos = firstOfMonth.getDay()-this.startDay;
25150
25151         if(startingPos <= this.startDay){
25152             startingPos += 7;
25153         }
25154
25155         var pm = date.add("mo", -1);
25156         var prevStart = pm.getDaysInMonth()-startingPos;
25157
25158         var cells = this.cells.elements;
25159         var textEls = this.textNodes;
25160         days += startingPos;
25161
25162         // convert everything to numbers so it's fast
25163         var day = 86400000;
25164         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
25165         var today = new Date().clearTime().getTime();
25166         var sel = date.clearTime().getTime();
25167         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
25168         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
25169         var ddMatch = this.disabledDatesRE;
25170         var ddText = this.disabledDatesText;
25171         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
25172         var ddaysText = this.disabledDaysText;
25173         var format = this.format;
25174
25175         var setCellClass = function(cal, cell){
25176             cell.title = "";
25177             var t = d.getTime();
25178             cell.firstChild.dateValue = t;
25179             if(t == today){
25180                 cell.className += " x-date-today";
25181                 cell.title = cal.todayText;
25182             }
25183             if(t == sel){
25184                 cell.className += " x-date-selected";
25185                 setTimeout(function(){
25186                     try{cell.firstChild.focus();}catch(e){}
25187                 }, 50);
25188             }
25189             // disabling
25190             if(t < min) {
25191                 cell.className = " x-date-disabled";
25192                 cell.title = cal.minText;
25193                 return;
25194             }
25195             if(t > max) {
25196                 cell.className = " x-date-disabled";
25197                 cell.title = cal.maxText;
25198                 return;
25199             }
25200             if(ddays){
25201                 if(ddays.indexOf(d.getDay()) != -1){
25202                     cell.title = ddaysText;
25203                     cell.className = " x-date-disabled";
25204                 }
25205             }
25206             if(ddMatch && format){
25207                 var fvalue = d.dateFormat(format);
25208                 if(ddMatch.test(fvalue)){
25209                     cell.title = ddText.replace("%0", fvalue);
25210                     cell.className = " x-date-disabled";
25211                 }
25212             }
25213         };
25214
25215         var i = 0;
25216         for(; i < startingPos; i++) {
25217             textEls[i].innerHTML = (++prevStart);
25218             d.setDate(d.getDate()+1);
25219             cells[i].className = "x-date-prevday";
25220             setCellClass(this, cells[i]);
25221         }
25222         for(; i < days; i++){
25223             intDay = i - startingPos + 1;
25224             textEls[i].innerHTML = (intDay);
25225             d.setDate(d.getDate()+1);
25226             cells[i].className = "x-date-active";
25227             setCellClass(this, cells[i]);
25228         }
25229         var extraDays = 0;
25230         for(; i < 42; i++) {
25231              textEls[i].innerHTML = (++extraDays);
25232              d.setDate(d.getDate()+1);
25233              cells[i].className = "x-date-nextday";
25234              setCellClass(this, cells[i]);
25235         }
25236
25237         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
25238         this.fireEvent('monthchange', this, date);
25239         
25240         if(!this.internalRender){
25241             var main = this.el.dom.firstChild;
25242             var w = main.offsetWidth;
25243             this.el.setWidth(w + this.el.getBorderWidth("lr"));
25244             Roo.fly(main).setWidth(w);
25245             this.internalRender = true;
25246             // opera does not respect the auto grow header center column
25247             // then, after it gets a width opera refuses to recalculate
25248             // without a second pass
25249             if(Roo.isOpera && !this.secondPass){
25250                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
25251                 this.secondPass = true;
25252                 this.update.defer(10, this, [date]);
25253             }
25254         }
25255         
25256         
25257     }
25258 });        /*
25259  * Based on:
25260  * Ext JS Library 1.1.1
25261  * Copyright(c) 2006-2007, Ext JS, LLC.
25262  *
25263  * Originally Released Under LGPL - original licence link has changed is not relivant.
25264  *
25265  * Fork - LGPL
25266  * <script type="text/javascript">
25267  */
25268 /**
25269  * @class Roo.TabPanel
25270  * @extends Roo.util.Observable
25271  * A lightweight tab container.
25272  * <br><br>
25273  * Usage:
25274  * <pre><code>
25275 // basic tabs 1, built from existing content
25276 var tabs = new Roo.TabPanel("tabs1");
25277 tabs.addTab("script", "View Script");
25278 tabs.addTab("markup", "View Markup");
25279 tabs.activate("script");
25280
25281 // more advanced tabs, built from javascript
25282 var jtabs = new Roo.TabPanel("jtabs");
25283 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
25284
25285 // set up the UpdateManager
25286 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
25287 var updater = tab2.getUpdateManager();
25288 updater.setDefaultUrl("ajax1.htm");
25289 tab2.on('activate', updater.refresh, updater, true);
25290
25291 // Use setUrl for Ajax loading
25292 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
25293 tab3.setUrl("ajax2.htm", null, true);
25294
25295 // Disabled tab
25296 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
25297 tab4.disable();
25298
25299 jtabs.activate("jtabs-1");
25300  * </code></pre>
25301  * @constructor
25302  * Create a new TabPanel.
25303  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
25304  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
25305  */
25306 Roo.TabPanel = function(container, config){
25307     /**
25308     * The container element for this TabPanel.
25309     * @type Roo.Element
25310     */
25311     this.el = Roo.get(container, true);
25312     if(config){
25313         if(typeof config == "boolean"){
25314             this.tabPosition = config ? "bottom" : "top";
25315         }else{
25316             Roo.apply(this, config);
25317         }
25318     }
25319     if(this.tabPosition == "bottom"){
25320         this.bodyEl = Roo.get(this.createBody(this.el.dom));
25321         this.el.addClass("x-tabs-bottom");
25322     }
25323     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
25324     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
25325     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
25326     if(Roo.isIE){
25327         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
25328     }
25329     if(this.tabPosition != "bottom"){
25330         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
25331          * @type Roo.Element
25332          */
25333         this.bodyEl = Roo.get(this.createBody(this.el.dom));
25334         this.el.addClass("x-tabs-top");
25335     }
25336     this.items = [];
25337
25338     this.bodyEl.setStyle("position", "relative");
25339
25340     this.active = null;
25341     this.activateDelegate = this.activate.createDelegate(this);
25342
25343     this.addEvents({
25344         /**
25345          * @event tabchange
25346          * Fires when the active tab changes
25347          * @param {Roo.TabPanel} this
25348          * @param {Roo.TabPanelItem} activePanel The new active tab
25349          */
25350         "tabchange": true,
25351         /**
25352          * @event beforetabchange
25353          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
25354          * @param {Roo.TabPanel} this
25355          * @param {Object} e Set cancel to true on this object to cancel the tab change
25356          * @param {Roo.TabPanelItem} tab The tab being changed to
25357          */
25358         "beforetabchange" : true
25359     });
25360
25361     Roo.EventManager.onWindowResize(this.onResize, this);
25362     this.cpad = this.el.getPadding("lr");
25363     this.hiddenCount = 0;
25364
25365
25366     // toolbar on the tabbar support...
25367     if (this.toolbar) {
25368         var tcfg = this.toolbar;
25369         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
25370         this.toolbar = new Roo.Toolbar(tcfg);
25371         if (Roo.isSafari) {
25372             var tbl = tcfg.container.child('table', true);
25373             tbl.setAttribute('width', '100%');
25374         }
25375         
25376     }
25377    
25378
25379
25380     Roo.TabPanel.superclass.constructor.call(this);
25381 };
25382
25383 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
25384     /*
25385      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
25386      */
25387     tabPosition : "top",
25388     /*
25389      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
25390      */
25391     currentTabWidth : 0,
25392     /*
25393      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
25394      */
25395     minTabWidth : 40,
25396     /*
25397      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
25398      */
25399     maxTabWidth : 250,
25400     /*
25401      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
25402      */
25403     preferredTabWidth : 175,
25404     /*
25405      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
25406      */
25407     resizeTabs : false,
25408     /*
25409      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
25410      */
25411     monitorResize : true,
25412     /*
25413      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
25414      */
25415     toolbar : false,
25416
25417     /**
25418      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
25419      * @param {String} id The id of the div to use <b>or create</b>
25420      * @param {String} text The text for the tab
25421      * @param {String} content (optional) Content to put in the TabPanelItem body
25422      * @param {Boolean} closable (optional) True to create a close icon on the tab
25423      * @return {Roo.TabPanelItem} The created TabPanelItem
25424      */
25425     addTab : function(id, text, content, closable){
25426         var item = new Roo.TabPanelItem(this, id, text, closable);
25427         this.addTabItem(item);
25428         if(content){
25429             item.setContent(content);
25430         }
25431         return item;
25432     },
25433
25434     /**
25435      * Returns the {@link Roo.TabPanelItem} with the specified id/index
25436      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
25437      * @return {Roo.TabPanelItem}
25438      */
25439     getTab : function(id){
25440         return this.items[id];
25441     },
25442
25443     /**
25444      * Hides the {@link Roo.TabPanelItem} with the specified id/index
25445      * @param {String/Number} id The id or index of the TabPanelItem to hide.
25446      */
25447     hideTab : function(id){
25448         var t = this.items[id];
25449         if(!t.isHidden()){
25450            t.setHidden(true);
25451            this.hiddenCount++;
25452            this.autoSizeTabs();
25453         }
25454     },
25455
25456     /**
25457      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
25458      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
25459      */
25460     unhideTab : function(id){
25461         var t = this.items[id];
25462         if(t.isHidden()){
25463            t.setHidden(false);
25464            this.hiddenCount--;
25465            this.autoSizeTabs();
25466         }
25467     },
25468
25469     /**
25470      * Adds an existing {@link Roo.TabPanelItem}.
25471      * @param {Roo.TabPanelItem} item The TabPanelItem to add
25472      */
25473     addTabItem : function(item){
25474         this.items[item.id] = item;
25475         this.items.push(item);
25476         if(this.resizeTabs){
25477            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
25478            this.autoSizeTabs();
25479         }else{
25480             item.autoSize();
25481         }
25482     },
25483
25484     /**
25485      * Removes a {@link Roo.TabPanelItem}.
25486      * @param {String/Number} id The id or index of the TabPanelItem to remove.
25487      */
25488     removeTab : function(id){
25489         var items = this.items;
25490         var tab = items[id];
25491         if(!tab) { return; }
25492         var index = items.indexOf(tab);
25493         if(this.active == tab && items.length > 1){
25494             var newTab = this.getNextAvailable(index);
25495             if(newTab) {
25496                 newTab.activate();
25497             }
25498         }
25499         this.stripEl.dom.removeChild(tab.pnode.dom);
25500         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
25501             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
25502         }
25503         items.splice(index, 1);
25504         delete this.items[tab.id];
25505         tab.fireEvent("close", tab);
25506         tab.purgeListeners();
25507         this.autoSizeTabs();
25508     },
25509
25510     getNextAvailable : function(start){
25511         var items = this.items;
25512         var index = start;
25513         // look for a next tab that will slide over to
25514         // replace the one being removed
25515         while(index < items.length){
25516             var item = items[++index];
25517             if(item && !item.isHidden()){
25518                 return item;
25519             }
25520         }
25521         // if one isn't found select the previous tab (on the left)
25522         index = start;
25523         while(index >= 0){
25524             var item = items[--index];
25525             if(item && !item.isHidden()){
25526                 return item;
25527             }
25528         }
25529         return null;
25530     },
25531
25532     /**
25533      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
25534      * @param {String/Number} id The id or index of the TabPanelItem to disable.
25535      */
25536     disableTab : function(id){
25537         var tab = this.items[id];
25538         if(tab && this.active != tab){
25539             tab.disable();
25540         }
25541     },
25542
25543     /**
25544      * Enables a {@link Roo.TabPanelItem} that is disabled.
25545      * @param {String/Number} id The id or index of the TabPanelItem to enable.
25546      */
25547     enableTab : function(id){
25548         var tab = this.items[id];
25549         tab.enable();
25550     },
25551
25552     /**
25553      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
25554      * @param {String/Number} id The id or index of the TabPanelItem to activate.
25555      * @return {Roo.TabPanelItem} The TabPanelItem.
25556      */
25557     activate : function(id){
25558         var tab = this.items[id];
25559         if(!tab){
25560             return null;
25561         }
25562         if(tab == this.active || tab.disabled){
25563             return tab;
25564         }
25565         var e = {};
25566         this.fireEvent("beforetabchange", this, e, tab);
25567         if(e.cancel !== true && !tab.disabled){
25568             if(this.active){
25569                 this.active.hide();
25570             }
25571             this.active = this.items[id];
25572             this.active.show();
25573             this.fireEvent("tabchange", this, this.active);
25574         }
25575         return tab;
25576     },
25577
25578     /**
25579      * Gets the active {@link Roo.TabPanelItem}.
25580      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
25581      */
25582     getActiveTab : function(){
25583         return this.active;
25584     },
25585
25586     /**
25587      * Updates the tab body element to fit the height of the container element
25588      * for overflow scrolling
25589      * @param {Number} targetHeight (optional) Override the starting height from the elements height
25590      */
25591     syncHeight : function(targetHeight){
25592         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
25593         var bm = this.bodyEl.getMargins();
25594         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
25595         this.bodyEl.setHeight(newHeight);
25596         return newHeight;
25597     },
25598
25599     onResize : function(){
25600         if(this.monitorResize){
25601             this.autoSizeTabs();
25602         }
25603     },
25604
25605     /**
25606      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
25607      */
25608     beginUpdate : function(){
25609         this.updating = true;
25610     },
25611
25612     /**
25613      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
25614      */
25615     endUpdate : function(){
25616         this.updating = false;
25617         this.autoSizeTabs();
25618     },
25619
25620     /**
25621      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
25622      */
25623     autoSizeTabs : function(){
25624         var count = this.items.length;
25625         var vcount = count - this.hiddenCount;
25626         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
25627         var w = Math.max(this.el.getWidth() - this.cpad, 10);
25628         var availWidth = Math.floor(w / vcount);
25629         var b = this.stripBody;
25630         if(b.getWidth() > w){
25631             var tabs = this.items;
25632             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
25633             if(availWidth < this.minTabWidth){
25634                 /*if(!this.sleft){    // incomplete scrolling code
25635                     this.createScrollButtons();
25636                 }
25637                 this.showScroll();
25638                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
25639             }
25640         }else{
25641             if(this.currentTabWidth < this.preferredTabWidth){
25642                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
25643             }
25644         }
25645     },
25646
25647     /**
25648      * Returns the number of tabs in this TabPanel.
25649      * @return {Number}
25650      */
25651      getCount : function(){
25652          return this.items.length;
25653      },
25654
25655     /**
25656      * Resizes all the tabs to the passed width
25657      * @param {Number} The new width
25658      */
25659     setTabWidth : function(width){
25660         this.currentTabWidth = width;
25661         for(var i = 0, len = this.items.length; i < len; i++) {
25662                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
25663         }
25664     },
25665
25666     /**
25667      * Destroys this TabPanel
25668      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
25669      */
25670     destroy : function(removeEl){
25671         Roo.EventManager.removeResizeListener(this.onResize, this);
25672         for(var i = 0, len = this.items.length; i < len; i++){
25673             this.items[i].purgeListeners();
25674         }
25675         if(removeEl === true){
25676             this.el.update("");
25677             this.el.remove();
25678         }
25679     }
25680 });
25681
25682 /**
25683  * @class Roo.TabPanelItem
25684  * @extends Roo.util.Observable
25685  * Represents an individual item (tab plus body) in a TabPanel.
25686  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
25687  * @param {String} id The id of this TabPanelItem
25688  * @param {String} text The text for the tab of this TabPanelItem
25689  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
25690  */
25691 Roo.TabPanelItem = function(tabPanel, id, text, closable){
25692     /**
25693      * The {@link Roo.TabPanel} this TabPanelItem belongs to
25694      * @type Roo.TabPanel
25695      */
25696     this.tabPanel = tabPanel;
25697     /**
25698      * The id for this TabPanelItem
25699      * @type String
25700      */
25701     this.id = id;
25702     /** @private */
25703     this.disabled = false;
25704     /** @private */
25705     this.text = text;
25706     /** @private */
25707     this.loaded = false;
25708     this.closable = closable;
25709
25710     /**
25711      * The body element for this TabPanelItem.
25712      * @type Roo.Element
25713      */
25714     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
25715     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
25716     this.bodyEl.setStyle("display", "block");
25717     this.bodyEl.setStyle("zoom", "1");
25718     this.hideAction();
25719
25720     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
25721     /** @private */
25722     this.el = Roo.get(els.el, true);
25723     this.inner = Roo.get(els.inner, true);
25724     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
25725     this.pnode = Roo.get(els.el.parentNode, true);
25726     this.el.on("mousedown", this.onTabMouseDown, this);
25727     this.el.on("click", this.onTabClick, this);
25728     /** @private */
25729     if(closable){
25730         var c = Roo.get(els.close, true);
25731         c.dom.title = this.closeText;
25732         c.addClassOnOver("close-over");
25733         c.on("click", this.closeClick, this);
25734      }
25735
25736     this.addEvents({
25737          /**
25738          * @event activate
25739          * Fires when this tab becomes the active tab.
25740          * @param {Roo.TabPanel} tabPanel The parent TabPanel
25741          * @param {Roo.TabPanelItem} this
25742          */
25743         "activate": true,
25744         /**
25745          * @event beforeclose
25746          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
25747          * @param {Roo.TabPanelItem} this
25748          * @param {Object} e Set cancel to true on this object to cancel the close.
25749          */
25750         "beforeclose": true,
25751         /**
25752          * @event close
25753          * Fires when this tab is closed.
25754          * @param {Roo.TabPanelItem} this
25755          */
25756          "close": true,
25757         /**
25758          * @event deactivate
25759          * Fires when this tab is no longer the active tab.
25760          * @param {Roo.TabPanel} tabPanel The parent TabPanel
25761          * @param {Roo.TabPanelItem} this
25762          */
25763          "deactivate" : true
25764     });
25765     this.hidden = false;
25766
25767     Roo.TabPanelItem.superclass.constructor.call(this);
25768 };
25769
25770 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
25771     purgeListeners : function(){
25772        Roo.util.Observable.prototype.purgeListeners.call(this);
25773        this.el.removeAllListeners();
25774     },
25775     /**
25776      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
25777      */
25778     show : function(){
25779         this.pnode.addClass("on");
25780         this.showAction();
25781         if(Roo.isOpera){
25782             this.tabPanel.stripWrap.repaint();
25783         }
25784         this.fireEvent("activate", this.tabPanel, this);
25785     },
25786
25787     /**
25788      * Returns true if this tab is the active tab.
25789      * @return {Boolean}
25790      */
25791     isActive : function(){
25792         return this.tabPanel.getActiveTab() == this;
25793     },
25794
25795     /**
25796      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
25797      */
25798     hide : function(){
25799         this.pnode.removeClass("on");
25800         this.hideAction();
25801         this.fireEvent("deactivate", this.tabPanel, this);
25802     },
25803
25804     hideAction : function(){
25805         this.bodyEl.hide();
25806         this.bodyEl.setStyle("position", "absolute");
25807         this.bodyEl.setLeft("-20000px");
25808         this.bodyEl.setTop("-20000px");
25809     },
25810
25811     showAction : function(){
25812         this.bodyEl.setStyle("position", "relative");
25813         this.bodyEl.setTop("");
25814         this.bodyEl.setLeft("");
25815         this.bodyEl.show();
25816     },
25817
25818     /**
25819      * Set the tooltip for the tab.
25820      * @param {String} tooltip The tab's tooltip
25821      */
25822     setTooltip : function(text){
25823         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
25824             this.textEl.dom.qtip = text;
25825             this.textEl.dom.removeAttribute('title');
25826         }else{
25827             this.textEl.dom.title = text;
25828         }
25829     },
25830
25831     onTabClick : function(e){
25832         e.preventDefault();
25833         this.tabPanel.activate(this.id);
25834     },
25835
25836     onTabMouseDown : function(e){
25837         e.preventDefault();
25838         this.tabPanel.activate(this.id);
25839     },
25840
25841     getWidth : function(){
25842         return this.inner.getWidth();
25843     },
25844
25845     setWidth : function(width){
25846         var iwidth = width - this.pnode.getPadding("lr");
25847         this.inner.setWidth(iwidth);
25848         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
25849         this.pnode.setWidth(width);
25850     },
25851
25852     /**
25853      * Show or hide the tab
25854      * @param {Boolean} hidden True to hide or false to show.
25855      */
25856     setHidden : function(hidden){
25857         this.hidden = hidden;
25858         this.pnode.setStyle("display", hidden ? "none" : "");
25859     },
25860
25861     /**
25862      * Returns true if this tab is "hidden"
25863      * @return {Boolean}
25864      */
25865     isHidden : function(){
25866         return this.hidden;
25867     },
25868
25869     /**
25870      * Returns the text for this tab
25871      * @return {String}
25872      */
25873     getText : function(){
25874         return this.text;
25875     },
25876
25877     autoSize : function(){
25878         //this.el.beginMeasure();
25879         this.textEl.setWidth(1);
25880         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
25881         //this.el.endMeasure();
25882     },
25883
25884     /**
25885      * Sets the text for the tab (Note: this also sets the tooltip text)
25886      * @param {String} text The tab's text and tooltip
25887      */
25888     setText : function(text){
25889         this.text = text;
25890         this.textEl.update(text);
25891         this.setTooltip(text);
25892         if(!this.tabPanel.resizeTabs){
25893             this.autoSize();
25894         }
25895     },
25896     /**
25897      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
25898      */
25899     activate : function(){
25900         this.tabPanel.activate(this.id);
25901     },
25902
25903     /**
25904      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
25905      */
25906     disable : function(){
25907         if(this.tabPanel.active != this){
25908             this.disabled = true;
25909             this.pnode.addClass("disabled");
25910         }
25911     },
25912
25913     /**
25914      * Enables this TabPanelItem if it was previously disabled.
25915      */
25916     enable : function(){
25917         this.disabled = false;
25918         this.pnode.removeClass("disabled");
25919     },
25920
25921     /**
25922      * Sets the content for this TabPanelItem.
25923      * @param {String} content The content
25924      * @param {Boolean} loadScripts true to look for and load scripts
25925      */
25926     setContent : function(content, loadScripts){
25927         this.bodyEl.update(content, loadScripts);
25928     },
25929
25930     /**
25931      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
25932      * @return {Roo.UpdateManager} The UpdateManager
25933      */
25934     getUpdateManager : function(){
25935         return this.bodyEl.getUpdateManager();
25936     },
25937
25938     /**
25939      * Set a URL to be used to load the content for this TabPanelItem.
25940      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
25941      * @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)
25942      * @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)
25943      * @return {Roo.UpdateManager} The UpdateManager
25944      */
25945     setUrl : function(url, params, loadOnce){
25946         if(this.refreshDelegate){
25947             this.un('activate', this.refreshDelegate);
25948         }
25949         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
25950         this.on("activate", this.refreshDelegate);
25951         return this.bodyEl.getUpdateManager();
25952     },
25953
25954     /** @private */
25955     _handleRefresh : function(url, params, loadOnce){
25956         if(!loadOnce || !this.loaded){
25957             var updater = this.bodyEl.getUpdateManager();
25958             updater.update(url, params, this._setLoaded.createDelegate(this));
25959         }
25960     },
25961
25962     /**
25963      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
25964      *   Will fail silently if the setUrl method has not been called.
25965      *   This does not activate the panel, just updates its content.
25966      */
25967     refresh : function(){
25968         if(this.refreshDelegate){
25969            this.loaded = false;
25970            this.refreshDelegate();
25971         }
25972     },
25973
25974     /** @private */
25975     _setLoaded : function(){
25976         this.loaded = true;
25977     },
25978
25979     /** @private */
25980     closeClick : function(e){
25981         var o = {};
25982         e.stopEvent();
25983         this.fireEvent("beforeclose", this, o);
25984         if(o.cancel !== true){
25985             this.tabPanel.removeTab(this.id);
25986         }
25987     },
25988     /**
25989      * The text displayed in the tooltip for the close icon.
25990      * @type String
25991      */
25992     closeText : "Close this tab"
25993 });
25994
25995 /** @private */
25996 Roo.TabPanel.prototype.createStrip = function(container){
25997     var strip = document.createElement("div");
25998     strip.className = "x-tabs-wrap";
25999     container.appendChild(strip);
26000     return strip;
26001 };
26002 /** @private */
26003 Roo.TabPanel.prototype.createStripList = function(strip){
26004     // div wrapper for retard IE
26005     // returns the "tr" element.
26006     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
26007         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
26008         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
26009     return strip.firstChild.firstChild.firstChild.firstChild;
26010 };
26011 /** @private */
26012 Roo.TabPanel.prototype.createBody = function(container){
26013     var body = document.createElement("div");
26014     Roo.id(body, "tab-body");
26015     Roo.fly(body).addClass("x-tabs-body");
26016     container.appendChild(body);
26017     return body;
26018 };
26019 /** @private */
26020 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
26021     var body = Roo.getDom(id);
26022     if(!body){
26023         body = document.createElement("div");
26024         body.id = id;
26025     }
26026     Roo.fly(body).addClass("x-tabs-item-body");
26027     bodyEl.insertBefore(body, bodyEl.firstChild);
26028     return body;
26029 };
26030 /** @private */
26031 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
26032     var td = document.createElement("td");
26033     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
26034     //stripEl.appendChild(td);
26035     if(closable){
26036         td.className = "x-tabs-closable";
26037         if(!this.closeTpl){
26038             this.closeTpl = 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>' +
26041                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
26042             );
26043         }
26044         var el = this.closeTpl.overwrite(td, {"text": text});
26045         var close = el.getElementsByTagName("div")[0];
26046         var inner = el.getElementsByTagName("em")[0];
26047         return {"el": el, "close": close, "inner": inner};
26048     } else {
26049         if(!this.tabTpl){
26050             this.tabTpl = new Roo.Template(
26051                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
26052                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
26053             );
26054         }
26055         var el = this.tabTpl.overwrite(td, {"text": text});
26056         var inner = el.getElementsByTagName("em")[0];
26057         return {"el": el, "inner": inner};
26058     }
26059 };/*
26060  * Based on:
26061  * Ext JS Library 1.1.1
26062  * Copyright(c) 2006-2007, Ext JS, LLC.
26063  *
26064  * Originally Released Under LGPL - original licence link has changed is not relivant.
26065  *
26066  * Fork - LGPL
26067  * <script type="text/javascript">
26068  */
26069
26070 /**
26071  * @class Roo.Button
26072  * @extends Roo.util.Observable
26073  * Simple Button class
26074  * @cfg {String} text The button text
26075  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
26076  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
26077  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
26078  * @cfg {Object} scope The scope of the handler
26079  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
26080  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
26081  * @cfg {Boolean} hidden True to start hidden (defaults to false)
26082  * @cfg {Boolean} disabled True to start disabled (defaults to false)
26083  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
26084  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
26085    applies if enableToggle = true)
26086  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
26087  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
26088   an {@link Roo.util.ClickRepeater} config object (defaults to false).
26089  * @constructor
26090  * Create a new button
26091  * @param {Object} config The config object
26092  */
26093 Roo.Button = function(renderTo, config)
26094 {
26095     if (!config) {
26096         config = renderTo;
26097         renderTo = config.renderTo || false;
26098     }
26099     
26100     Roo.apply(this, config);
26101     this.addEvents({
26102         /**
26103              * @event click
26104              * Fires when this button is clicked
26105              * @param {Button} this
26106              * @param {EventObject} e The click event
26107              */
26108             "click" : true,
26109         /**
26110              * @event toggle
26111              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
26112              * @param {Button} this
26113              * @param {Boolean} pressed
26114              */
26115             "toggle" : true,
26116         /**
26117              * @event mouseover
26118              * Fires when the mouse hovers over the button
26119              * @param {Button} this
26120              * @param {Event} e The event object
26121              */
26122         'mouseover' : true,
26123         /**
26124              * @event mouseout
26125              * Fires when the mouse exits the button
26126              * @param {Button} this
26127              * @param {Event} e The event object
26128              */
26129         'mouseout': true,
26130          /**
26131              * @event render
26132              * Fires when the button is rendered
26133              * @param {Button} this
26134              */
26135         'render': true
26136     });
26137     if(this.menu){
26138         this.menu = Roo.menu.MenuMgr.get(this.menu);
26139     }
26140     // register listeners first!!  - so render can be captured..
26141     Roo.util.Observable.call(this);
26142     if(renderTo){
26143         this.render(renderTo);
26144     }
26145     
26146   
26147 };
26148
26149 Roo.extend(Roo.Button, Roo.util.Observable, {
26150     /**
26151      * 
26152      */
26153     
26154     /**
26155      * Read-only. True if this button is hidden
26156      * @type Boolean
26157      */
26158     hidden : false,
26159     /**
26160      * Read-only. True if this button is disabled
26161      * @type Boolean
26162      */
26163     disabled : false,
26164     /**
26165      * Read-only. True if this button is pressed (only if enableToggle = true)
26166      * @type Boolean
26167      */
26168     pressed : false,
26169
26170     /**
26171      * @cfg {Number} tabIndex 
26172      * The DOM tabIndex for this button (defaults to undefined)
26173      */
26174     tabIndex : undefined,
26175
26176     /**
26177      * @cfg {Boolean} enableToggle
26178      * True to enable pressed/not pressed toggling (defaults to false)
26179      */
26180     enableToggle: false,
26181     /**
26182      * @cfg {Mixed} menu
26183      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
26184      */
26185     menu : undefined,
26186     /**
26187      * @cfg {String} menuAlign
26188      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
26189      */
26190     menuAlign : "tl-bl?",
26191
26192     /**
26193      * @cfg {String} iconCls
26194      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
26195      */
26196     iconCls : undefined,
26197     /**
26198      * @cfg {String} type
26199      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
26200      */
26201     type : 'button',
26202
26203     // private
26204     menuClassTarget: 'tr',
26205
26206     /**
26207      * @cfg {String} clickEvent
26208      * The type of event to map to the button's event handler (defaults to 'click')
26209      */
26210     clickEvent : 'click',
26211
26212     /**
26213      * @cfg {Boolean} handleMouseEvents
26214      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
26215      */
26216     handleMouseEvents : true,
26217
26218     /**
26219      * @cfg {String} tooltipType
26220      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
26221      */
26222     tooltipType : 'qtip',
26223
26224     /**
26225      * @cfg {String} cls
26226      * A CSS class to apply to the button's main element.
26227      */
26228     
26229     /**
26230      * @cfg {Roo.Template} template (Optional)
26231      * An {@link Roo.Template} with which to create the Button's main element. This Template must
26232      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
26233      * require code modifications if required elements (e.g. a button) aren't present.
26234      */
26235
26236     // private
26237     render : function(renderTo){
26238         var btn;
26239         if(this.hideParent){
26240             this.parentEl = Roo.get(renderTo);
26241         }
26242         if(!this.dhconfig){
26243             if(!this.template){
26244                 if(!Roo.Button.buttonTemplate){
26245                     // hideous table template
26246                     Roo.Button.buttonTemplate = new Roo.Template(
26247                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
26248                         '<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>',
26249                         "</tr></tbody></table>");
26250                 }
26251                 this.template = Roo.Button.buttonTemplate;
26252             }
26253             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
26254             var btnEl = btn.child("button:first");
26255             btnEl.on('focus', this.onFocus, this);
26256             btnEl.on('blur', this.onBlur, this);
26257             if(this.cls){
26258                 btn.addClass(this.cls);
26259             }
26260             if(this.icon){
26261                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
26262             }
26263             if(this.iconCls){
26264                 btnEl.addClass(this.iconCls);
26265                 if(!this.cls){
26266                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
26267                 }
26268             }
26269             if(this.tabIndex !== undefined){
26270                 btnEl.dom.tabIndex = this.tabIndex;
26271             }
26272             if(this.tooltip){
26273                 if(typeof this.tooltip == 'object'){
26274                     Roo.QuickTips.tips(Roo.apply({
26275                           target: btnEl.id
26276                     }, this.tooltip));
26277                 } else {
26278                     btnEl.dom[this.tooltipType] = this.tooltip;
26279                 }
26280             }
26281         }else{
26282             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
26283         }
26284         this.el = btn;
26285         if(this.id){
26286             this.el.dom.id = this.el.id = this.id;
26287         }
26288         if(this.menu){
26289             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
26290             this.menu.on("show", this.onMenuShow, this);
26291             this.menu.on("hide", this.onMenuHide, this);
26292         }
26293         btn.addClass("x-btn");
26294         if(Roo.isIE && !Roo.isIE7){
26295             this.autoWidth.defer(1, this);
26296         }else{
26297             this.autoWidth();
26298         }
26299         if(this.handleMouseEvents){
26300             btn.on("mouseover", this.onMouseOver, this);
26301             btn.on("mouseout", this.onMouseOut, this);
26302             btn.on("mousedown", this.onMouseDown, this);
26303         }
26304         btn.on(this.clickEvent, this.onClick, this);
26305         //btn.on("mouseup", this.onMouseUp, this);
26306         if(this.hidden){
26307             this.hide();
26308         }
26309         if(this.disabled){
26310             this.disable();
26311         }
26312         Roo.ButtonToggleMgr.register(this);
26313         if(this.pressed){
26314             this.el.addClass("x-btn-pressed");
26315         }
26316         if(this.repeat){
26317             var repeater = new Roo.util.ClickRepeater(btn,
26318                 typeof this.repeat == "object" ? this.repeat : {}
26319             );
26320             repeater.on("click", this.onClick,  this);
26321         }
26322         
26323         this.fireEvent('render', this);
26324         
26325     },
26326     /**
26327      * Returns the button's underlying element
26328      * @return {Roo.Element} The element
26329      */
26330     getEl : function(){
26331         return this.el;  
26332     },
26333     
26334     /**
26335      * Destroys this Button and removes any listeners.
26336      */
26337     destroy : function(){
26338         Roo.ButtonToggleMgr.unregister(this);
26339         this.el.removeAllListeners();
26340         this.purgeListeners();
26341         this.el.remove();
26342     },
26343
26344     // private
26345     autoWidth : function(){
26346         if(this.el){
26347             this.el.setWidth("auto");
26348             if(Roo.isIE7 && Roo.isStrict){
26349                 var ib = this.el.child('button');
26350                 if(ib && ib.getWidth() > 20){
26351                     ib.clip();
26352                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
26353                 }
26354             }
26355             if(this.minWidth){
26356                 if(this.hidden){
26357                     this.el.beginMeasure();
26358                 }
26359                 if(this.el.getWidth() < this.minWidth){
26360                     this.el.setWidth(this.minWidth);
26361                 }
26362                 if(this.hidden){
26363                     this.el.endMeasure();
26364                 }
26365             }
26366         }
26367     },
26368
26369     /**
26370      * Assigns this button's click handler
26371      * @param {Function} handler The function to call when the button is clicked
26372      * @param {Object} scope (optional) Scope for the function passed in
26373      */
26374     setHandler : function(handler, scope){
26375         this.handler = handler;
26376         this.scope = scope;  
26377     },
26378     
26379     /**
26380      * Sets this button's text
26381      * @param {String} text The button text
26382      */
26383     setText : function(text){
26384         this.text = text;
26385         if(this.el){
26386             this.el.child("td.x-btn-center button.x-btn-text").update(text);
26387         }
26388         this.autoWidth();
26389     },
26390     
26391     /**
26392      * Gets the text for this button
26393      * @return {String} The button text
26394      */
26395     getText : function(){
26396         return this.text;  
26397     },
26398     
26399     /**
26400      * Show this button
26401      */
26402     show: function(){
26403         this.hidden = false;
26404         if(this.el){
26405             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
26406         }
26407     },
26408     
26409     /**
26410      * Hide this button
26411      */
26412     hide: function(){
26413         this.hidden = true;
26414         if(this.el){
26415             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
26416         }
26417     },
26418     
26419     /**
26420      * Convenience function for boolean show/hide
26421      * @param {Boolean} visible True to show, false to hide
26422      */
26423     setVisible: function(visible){
26424         if(visible) {
26425             this.show();
26426         }else{
26427             this.hide();
26428         }
26429     },
26430     
26431     /**
26432      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
26433      * @param {Boolean} state (optional) Force a particular state
26434      */
26435     toggle : function(state){
26436         state = state === undefined ? !this.pressed : state;
26437         if(state != this.pressed){
26438             if(state){
26439                 this.el.addClass("x-btn-pressed");
26440                 this.pressed = true;
26441                 this.fireEvent("toggle", this, true);
26442             }else{
26443                 this.el.removeClass("x-btn-pressed");
26444                 this.pressed = false;
26445                 this.fireEvent("toggle", this, false);
26446             }
26447             if(this.toggleHandler){
26448                 this.toggleHandler.call(this.scope || this, this, state);
26449             }
26450         }
26451     },
26452     
26453     /**
26454      * Focus the button
26455      */
26456     focus : function(){
26457         this.el.child('button:first').focus();
26458     },
26459     
26460     /**
26461      * Disable this button
26462      */
26463     disable : function(){
26464         if(this.el){
26465             this.el.addClass("x-btn-disabled");
26466         }
26467         this.disabled = true;
26468     },
26469     
26470     /**
26471      * Enable this button
26472      */
26473     enable : function(){
26474         if(this.el){
26475             this.el.removeClass("x-btn-disabled");
26476         }
26477         this.disabled = false;
26478     },
26479
26480     /**
26481      * Convenience function for boolean enable/disable
26482      * @param {Boolean} enabled True to enable, false to disable
26483      */
26484     setDisabled : function(v){
26485         this[v !== true ? "enable" : "disable"]();
26486     },
26487
26488     // private
26489     onClick : function(e){
26490         if(e){
26491             e.preventDefault();
26492         }
26493         if(e.button != 0){
26494             return;
26495         }
26496         if(!this.disabled){
26497             if(this.enableToggle){
26498                 this.toggle();
26499             }
26500             if(this.menu && !this.menu.isVisible()){
26501                 this.menu.show(this.el, this.menuAlign);
26502             }
26503             this.fireEvent("click", this, e);
26504             if(this.handler){
26505                 this.el.removeClass("x-btn-over");
26506                 this.handler.call(this.scope || this, this, e);
26507             }
26508         }
26509     },
26510     // private
26511     onMouseOver : function(e){
26512         if(!this.disabled){
26513             this.el.addClass("x-btn-over");
26514             this.fireEvent('mouseover', this, e);
26515         }
26516     },
26517     // private
26518     onMouseOut : function(e){
26519         if(!e.within(this.el,  true)){
26520             this.el.removeClass("x-btn-over");
26521             this.fireEvent('mouseout', this, e);
26522         }
26523     },
26524     // private
26525     onFocus : function(e){
26526         if(!this.disabled){
26527             this.el.addClass("x-btn-focus");
26528         }
26529     },
26530     // private
26531     onBlur : function(e){
26532         this.el.removeClass("x-btn-focus");
26533     },
26534     // private
26535     onMouseDown : function(e){
26536         if(!this.disabled && e.button == 0){
26537             this.el.addClass("x-btn-click");
26538             Roo.get(document).on('mouseup', this.onMouseUp, this);
26539         }
26540     },
26541     // private
26542     onMouseUp : function(e){
26543         if(e.button == 0){
26544             this.el.removeClass("x-btn-click");
26545             Roo.get(document).un('mouseup', this.onMouseUp, this);
26546         }
26547     },
26548     // private
26549     onMenuShow : function(e){
26550         this.el.addClass("x-btn-menu-active");
26551     },
26552     // private
26553     onMenuHide : function(e){
26554         this.el.removeClass("x-btn-menu-active");
26555     }   
26556 });
26557
26558 // Private utility class used by Button
26559 Roo.ButtonToggleMgr = function(){
26560    var groups = {};
26561    
26562    function toggleGroup(btn, state){
26563        if(state){
26564            var g = groups[btn.toggleGroup];
26565            for(var i = 0, l = g.length; i < l; i++){
26566                if(g[i] != btn){
26567                    g[i].toggle(false);
26568                }
26569            }
26570        }
26571    }
26572    
26573    return {
26574        register : function(btn){
26575            if(!btn.toggleGroup){
26576                return;
26577            }
26578            var g = groups[btn.toggleGroup];
26579            if(!g){
26580                g = groups[btn.toggleGroup] = [];
26581            }
26582            g.push(btn);
26583            btn.on("toggle", toggleGroup);
26584        },
26585        
26586        unregister : function(btn){
26587            if(!btn.toggleGroup){
26588                return;
26589            }
26590            var g = groups[btn.toggleGroup];
26591            if(g){
26592                g.remove(btn);
26593                btn.un("toggle", toggleGroup);
26594            }
26595        }
26596    };
26597 }();/*
26598  * Based on:
26599  * Ext JS Library 1.1.1
26600  * Copyright(c) 2006-2007, Ext JS, LLC.
26601  *
26602  * Originally Released Under LGPL - original licence link has changed is not relivant.
26603  *
26604  * Fork - LGPL
26605  * <script type="text/javascript">
26606  */
26607  
26608 /**
26609  * @class Roo.SplitButton
26610  * @extends Roo.Button
26611  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
26612  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
26613  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
26614  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
26615  * @cfg {String} arrowTooltip The title attribute of the arrow
26616  * @constructor
26617  * Create a new menu button
26618  * @param {String/HTMLElement/Element} renderTo The element to append the button to
26619  * @param {Object} config The config object
26620  */
26621 Roo.SplitButton = function(renderTo, config){
26622     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
26623     /**
26624      * @event arrowclick
26625      * Fires when this button's arrow is clicked
26626      * @param {SplitButton} this
26627      * @param {EventObject} e The click event
26628      */
26629     this.addEvents({"arrowclick":true});
26630 };
26631
26632 Roo.extend(Roo.SplitButton, Roo.Button, {
26633     render : function(renderTo){
26634         // this is one sweet looking template!
26635         var tpl = new Roo.Template(
26636             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
26637             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
26638             '<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>',
26639             "</tbody></table></td><td>",
26640             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
26641             '<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>',
26642             "</tbody></table></td></tr></table>"
26643         );
26644         var btn = tpl.append(renderTo, [this.text, this.type], true);
26645         var btnEl = btn.child("button");
26646         if(this.cls){
26647             btn.addClass(this.cls);
26648         }
26649         if(this.icon){
26650             btnEl.setStyle('background-image', 'url(' +this.icon +')');
26651         }
26652         if(this.iconCls){
26653             btnEl.addClass(this.iconCls);
26654             if(!this.cls){
26655                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
26656             }
26657         }
26658         this.el = btn;
26659         if(this.handleMouseEvents){
26660             btn.on("mouseover", this.onMouseOver, this);
26661             btn.on("mouseout", this.onMouseOut, this);
26662             btn.on("mousedown", this.onMouseDown, this);
26663             btn.on("mouseup", this.onMouseUp, this);
26664         }
26665         btn.on(this.clickEvent, this.onClick, this);
26666         if(this.tooltip){
26667             if(typeof this.tooltip == 'object'){
26668                 Roo.QuickTips.tips(Roo.apply({
26669                       target: btnEl.id
26670                 }, this.tooltip));
26671             } else {
26672                 btnEl.dom[this.tooltipType] = this.tooltip;
26673             }
26674         }
26675         if(this.arrowTooltip){
26676             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
26677         }
26678         if(this.hidden){
26679             this.hide();
26680         }
26681         if(this.disabled){
26682             this.disable();
26683         }
26684         if(this.pressed){
26685             this.el.addClass("x-btn-pressed");
26686         }
26687         if(Roo.isIE && !Roo.isIE7){
26688             this.autoWidth.defer(1, this);
26689         }else{
26690             this.autoWidth();
26691         }
26692         if(this.menu){
26693             this.menu.on("show", this.onMenuShow, this);
26694             this.menu.on("hide", this.onMenuHide, this);
26695         }
26696         this.fireEvent('render', this);
26697     },
26698
26699     // private
26700     autoWidth : function(){
26701         if(this.el){
26702             var tbl = this.el.child("table:first");
26703             var tbl2 = this.el.child("table:last");
26704             this.el.setWidth("auto");
26705             tbl.setWidth("auto");
26706             if(Roo.isIE7 && Roo.isStrict){
26707                 var ib = this.el.child('button:first');
26708                 if(ib && ib.getWidth() > 20){
26709                     ib.clip();
26710                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
26711                 }
26712             }
26713             if(this.minWidth){
26714                 if(this.hidden){
26715                     this.el.beginMeasure();
26716                 }
26717                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
26718                     tbl.setWidth(this.minWidth-tbl2.getWidth());
26719                 }
26720                 if(this.hidden){
26721                     this.el.endMeasure();
26722                 }
26723             }
26724             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
26725         } 
26726     },
26727     /**
26728      * Sets this button's click handler
26729      * @param {Function} handler The function to call when the button is clicked
26730      * @param {Object} scope (optional) Scope for the function passed above
26731      */
26732     setHandler : function(handler, scope){
26733         this.handler = handler;
26734         this.scope = scope;  
26735     },
26736     
26737     /**
26738      * Sets this button's arrow click handler
26739      * @param {Function} handler The function to call when the arrow is clicked
26740      * @param {Object} scope (optional) Scope for the function passed above
26741      */
26742     setArrowHandler : function(handler, scope){
26743         this.arrowHandler = handler;
26744         this.scope = scope;  
26745     },
26746     
26747     /**
26748      * Focus the button
26749      */
26750     focus : function(){
26751         if(this.el){
26752             this.el.child("button:first").focus();
26753         }
26754     },
26755
26756     // private
26757     onClick : function(e){
26758         e.preventDefault();
26759         if(!this.disabled){
26760             if(e.getTarget(".x-btn-menu-arrow-wrap")){
26761                 if(this.menu && !this.menu.isVisible()){
26762                     this.menu.show(this.el, this.menuAlign);
26763                 }
26764                 this.fireEvent("arrowclick", this, e);
26765                 if(this.arrowHandler){
26766                     this.arrowHandler.call(this.scope || this, this, e);
26767                 }
26768             }else{
26769                 this.fireEvent("click", this, e);
26770                 if(this.handler){
26771                     this.handler.call(this.scope || this, this, e);
26772                 }
26773             }
26774         }
26775     },
26776     // private
26777     onMouseDown : function(e){
26778         if(!this.disabled){
26779             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
26780         }
26781     },
26782     // private
26783     onMouseUp : function(e){
26784         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
26785     }   
26786 });
26787
26788
26789 // backwards compat
26790 Roo.MenuButton = Roo.SplitButton;/*
26791  * Based on:
26792  * Ext JS Library 1.1.1
26793  * Copyright(c) 2006-2007, Ext JS, LLC.
26794  *
26795  * Originally Released Under LGPL - original licence link has changed is not relivant.
26796  *
26797  * Fork - LGPL
26798  * <script type="text/javascript">
26799  */
26800
26801 /**
26802  * @class Roo.Toolbar
26803  * Basic Toolbar class.
26804  * @constructor
26805  * Creates a new Toolbar
26806  * @param {Object} container The config object
26807  */ 
26808 Roo.Toolbar = function(container, buttons, config)
26809 {
26810     /// old consturctor format still supported..
26811     if(container instanceof Array){ // omit the container for later rendering
26812         buttons = container;
26813         config = buttons;
26814         container = null;
26815     }
26816     if (typeof(container) == 'object' && container.xtype) {
26817         config = container;
26818         container = config.container;
26819         buttons = config.buttons || []; // not really - use items!!
26820     }
26821     var xitems = [];
26822     if (config && config.items) {
26823         xitems = config.items;
26824         delete config.items;
26825     }
26826     Roo.apply(this, config);
26827     this.buttons = buttons;
26828     
26829     if(container){
26830         this.render(container);
26831     }
26832     this.xitems = xitems;
26833     Roo.each(xitems, function(b) {
26834         this.add(b);
26835     }, this);
26836     
26837 };
26838
26839 Roo.Toolbar.prototype = {
26840     /**
26841      * @cfg {Array} items
26842      * array of button configs or elements to add (will be converted to a MixedCollection)
26843      */
26844     
26845     /**
26846      * @cfg {String/HTMLElement/Element} container
26847      * The id or element that will contain the toolbar
26848      */
26849     // private
26850     render : function(ct){
26851         this.el = Roo.get(ct);
26852         if(this.cls){
26853             this.el.addClass(this.cls);
26854         }
26855         // using a table allows for vertical alignment
26856         // 100% width is needed by Safari...
26857         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
26858         this.tr = this.el.child("tr", true);
26859         var autoId = 0;
26860         this.items = new Roo.util.MixedCollection(false, function(o){
26861             return o.id || ("item" + (++autoId));
26862         });
26863         if(this.buttons){
26864             this.add.apply(this, this.buttons);
26865             delete this.buttons;
26866         }
26867     },
26868
26869     /**
26870      * Adds element(s) to the toolbar -- this function takes a variable number of 
26871      * arguments of mixed type and adds them to the toolbar.
26872      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
26873      * <ul>
26874      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
26875      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
26876      * <li>Field: Any form field (equivalent to {@link #addField})</li>
26877      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
26878      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
26879      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
26880      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
26881      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
26882      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
26883      * </ul>
26884      * @param {Mixed} arg2
26885      * @param {Mixed} etc.
26886      */
26887     add : function(){
26888         var a = arguments, l = a.length;
26889         for(var i = 0; i < l; i++){
26890             this._add(a[i]);
26891         }
26892     },
26893     // private..
26894     _add : function(el) {
26895         
26896         if (el.xtype) {
26897             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
26898         }
26899         
26900         if (el.applyTo){ // some kind of form field
26901             return this.addField(el);
26902         } 
26903         if (el.render){ // some kind of Toolbar.Item
26904             return this.addItem(el);
26905         }
26906         if (typeof el == "string"){ // string
26907             if(el == "separator" || el == "-"){
26908                 return this.addSeparator();
26909             }
26910             if (el == " "){
26911                 return this.addSpacer();
26912             }
26913             if(el == "->"){
26914                 return this.addFill();
26915             }
26916             return this.addText(el);
26917             
26918         }
26919         if(el.tagName){ // element
26920             return this.addElement(el);
26921         }
26922         if(typeof el == "object"){ // must be button config?
26923             return this.addButton(el);
26924         }
26925         // and now what?!?!
26926         return false;
26927         
26928     },
26929     
26930     /**
26931      * Add an Xtype element
26932      * @param {Object} xtype Xtype Object
26933      * @return {Object} created Object
26934      */
26935     addxtype : function(e){
26936         return this.add(e);  
26937     },
26938     
26939     /**
26940      * Returns the Element for this toolbar.
26941      * @return {Roo.Element}
26942      */
26943     getEl : function(){
26944         return this.el;  
26945     },
26946     
26947     /**
26948      * Adds a separator
26949      * @return {Roo.Toolbar.Item} The separator item
26950      */
26951     addSeparator : function(){
26952         return this.addItem(new Roo.Toolbar.Separator());
26953     },
26954
26955     /**
26956      * Adds a spacer element
26957      * @return {Roo.Toolbar.Spacer} The spacer item
26958      */
26959     addSpacer : function(){
26960         return this.addItem(new Roo.Toolbar.Spacer());
26961     },
26962
26963     /**
26964      * Adds a fill element that forces subsequent additions to the right side of the toolbar
26965      * @return {Roo.Toolbar.Fill} The fill item
26966      */
26967     addFill : function(){
26968         return this.addItem(new Roo.Toolbar.Fill());
26969     },
26970
26971     /**
26972      * Adds any standard HTML element to the toolbar
26973      * @param {String/HTMLElement/Element} el The element or id of the element to add
26974      * @return {Roo.Toolbar.Item} The element's item
26975      */
26976     addElement : function(el){
26977         return this.addItem(new Roo.Toolbar.Item(el));
26978     },
26979     /**
26980      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
26981      * @type Roo.util.MixedCollection  
26982      */
26983     items : false,
26984      
26985     /**
26986      * Adds any Toolbar.Item or subclass
26987      * @param {Roo.Toolbar.Item} item
26988      * @return {Roo.Toolbar.Item} The item
26989      */
26990     addItem : function(item){
26991         var td = this.nextBlock();
26992         item.render(td);
26993         this.items.add(item);
26994         return item;
26995     },
26996     
26997     /**
26998      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
26999      * @param {Object/Array} config A button config or array of configs
27000      * @return {Roo.Toolbar.Button/Array}
27001      */
27002     addButton : function(config){
27003         if(config instanceof Array){
27004             var buttons = [];
27005             for(var i = 0, len = config.length; i < len; i++) {
27006                 buttons.push(this.addButton(config[i]));
27007             }
27008             return buttons;
27009         }
27010         var b = config;
27011         if(!(config instanceof Roo.Toolbar.Button)){
27012             b = config.split ?
27013                 new Roo.Toolbar.SplitButton(config) :
27014                 new Roo.Toolbar.Button(config);
27015         }
27016         var td = this.nextBlock();
27017         b.render(td);
27018         this.items.add(b);
27019         return b;
27020     },
27021     
27022     /**
27023      * Adds text to the toolbar
27024      * @param {String} text The text to add
27025      * @return {Roo.Toolbar.Item} The element's item
27026      */
27027     addText : function(text){
27028         return this.addItem(new Roo.Toolbar.TextItem(text));
27029     },
27030     
27031     /**
27032      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
27033      * @param {Number} index The index where the item is to be inserted
27034      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
27035      * @return {Roo.Toolbar.Button/Item}
27036      */
27037     insertButton : function(index, item){
27038         if(item instanceof Array){
27039             var buttons = [];
27040             for(var i = 0, len = item.length; i < len; i++) {
27041                buttons.push(this.insertButton(index + i, item[i]));
27042             }
27043             return buttons;
27044         }
27045         if (!(item instanceof Roo.Toolbar.Button)){
27046            item = new Roo.Toolbar.Button(item);
27047         }
27048         var td = document.createElement("td");
27049         this.tr.insertBefore(td, this.tr.childNodes[index]);
27050         item.render(td);
27051         this.items.insert(index, item);
27052         return item;
27053     },
27054     
27055     /**
27056      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
27057      * @param {Object} config
27058      * @return {Roo.Toolbar.Item} The element's item
27059      */
27060     addDom : function(config, returnEl){
27061         var td = this.nextBlock();
27062         Roo.DomHelper.overwrite(td, config);
27063         var ti = new Roo.Toolbar.Item(td.firstChild);
27064         ti.render(td);
27065         this.items.add(ti);
27066         return ti;
27067     },
27068
27069     /**
27070      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
27071      * @type Roo.util.MixedCollection  
27072      */
27073     fields : false,
27074     
27075     /**
27076      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
27077      * Note: the field should not have been rendered yet. For a field that has already been
27078      * rendered, use {@link #addElement}.
27079      * @param {Roo.form.Field} field
27080      * @return {Roo.ToolbarItem}
27081      */
27082      
27083       
27084     addField : function(field) {
27085         if (!this.fields) {
27086             var autoId = 0;
27087             this.fields = new Roo.util.MixedCollection(false, function(o){
27088                 return o.id || ("item" + (++autoId));
27089             });
27090
27091         }
27092         
27093         var td = this.nextBlock();
27094         field.render(td);
27095         var ti = new Roo.Toolbar.Item(td.firstChild);
27096         ti.render(td);
27097         this.items.add(ti);
27098         this.fields.add(field);
27099         return ti;
27100     },
27101     /**
27102      * Hide the toolbar
27103      * @method hide
27104      */
27105      
27106       
27107     hide : function()
27108     {
27109         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
27110         this.el.child('div').hide();
27111     },
27112     /**
27113      * Show the toolbar
27114      * @method show
27115      */
27116     show : function()
27117     {
27118         this.el.child('div').show();
27119     },
27120       
27121     // private
27122     nextBlock : function(){
27123         var td = document.createElement("td");
27124         this.tr.appendChild(td);
27125         return td;
27126     },
27127
27128     // private
27129     destroy : function(){
27130         if(this.items){ // rendered?
27131             Roo.destroy.apply(Roo, this.items.items);
27132         }
27133         if(this.fields){ // rendered?
27134             Roo.destroy.apply(Roo, this.fields.items);
27135         }
27136         Roo.Element.uncache(this.el, this.tr);
27137     }
27138 };
27139
27140 /**
27141  * @class Roo.Toolbar.Item
27142  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
27143  * @constructor
27144  * Creates a new Item
27145  * @param {HTMLElement} el 
27146  */
27147 Roo.Toolbar.Item = function(el){
27148     this.el = Roo.getDom(el);
27149     this.id = Roo.id(this.el);
27150     this.hidden = false;
27151 };
27152
27153 Roo.Toolbar.Item.prototype = {
27154     
27155     /**
27156      * Get this item's HTML Element
27157      * @return {HTMLElement}
27158      */
27159     getEl : function(){
27160        return this.el;  
27161     },
27162
27163     // private
27164     render : function(td){
27165         this.td = td;
27166         td.appendChild(this.el);
27167     },
27168     
27169     /**
27170      * Removes and destroys this item.
27171      */
27172     destroy : function(){
27173         this.td.parentNode.removeChild(this.td);
27174     },
27175     
27176     /**
27177      * Shows this item.
27178      */
27179     show: function(){
27180         this.hidden = false;
27181         this.td.style.display = "";
27182     },
27183     
27184     /**
27185      * Hides this item.
27186      */
27187     hide: function(){
27188         this.hidden = true;
27189         this.td.style.display = "none";
27190     },
27191     
27192     /**
27193      * Convenience function for boolean show/hide.
27194      * @param {Boolean} visible true to show/false to hide
27195      */
27196     setVisible: function(visible){
27197         if(visible) {
27198             this.show();
27199         }else{
27200             this.hide();
27201         }
27202     },
27203     
27204     /**
27205      * Try to focus this item.
27206      */
27207     focus : function(){
27208         Roo.fly(this.el).focus();
27209     },
27210     
27211     /**
27212      * Disables this item.
27213      */
27214     disable : function(){
27215         Roo.fly(this.td).addClass("x-item-disabled");
27216         this.disabled = true;
27217         this.el.disabled = true;
27218     },
27219     
27220     /**
27221      * Enables this item.
27222      */
27223     enable : function(){
27224         Roo.fly(this.td).removeClass("x-item-disabled");
27225         this.disabled = false;
27226         this.el.disabled = false;
27227     }
27228 };
27229
27230
27231 /**
27232  * @class Roo.Toolbar.Separator
27233  * @extends Roo.Toolbar.Item
27234  * A simple toolbar separator class
27235  * @constructor
27236  * Creates a new Separator
27237  */
27238 Roo.Toolbar.Separator = function(){
27239     var s = document.createElement("span");
27240     s.className = "ytb-sep";
27241     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
27242 };
27243 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
27244     enable:Roo.emptyFn,
27245     disable:Roo.emptyFn,
27246     focus:Roo.emptyFn
27247 });
27248
27249 /**
27250  * @class Roo.Toolbar.Spacer
27251  * @extends Roo.Toolbar.Item
27252  * A simple element that adds extra horizontal space to a toolbar.
27253  * @constructor
27254  * Creates a new Spacer
27255  */
27256 Roo.Toolbar.Spacer = function(){
27257     var s = document.createElement("div");
27258     s.className = "ytb-spacer";
27259     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
27260 };
27261 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
27262     enable:Roo.emptyFn,
27263     disable:Roo.emptyFn,
27264     focus:Roo.emptyFn
27265 });
27266
27267 /**
27268  * @class Roo.Toolbar.Fill
27269  * @extends Roo.Toolbar.Spacer
27270  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
27271  * @constructor
27272  * Creates a new Spacer
27273  */
27274 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
27275     // private
27276     render : function(td){
27277         td.style.width = '100%';
27278         Roo.Toolbar.Fill.superclass.render.call(this, td);
27279     }
27280 });
27281
27282 /**
27283  * @class Roo.Toolbar.TextItem
27284  * @extends Roo.Toolbar.Item
27285  * A simple class that renders text directly into a toolbar.
27286  * @constructor
27287  * Creates a new TextItem
27288  * @param {String} text
27289  */
27290 Roo.Toolbar.TextItem = function(text){
27291     if (typeof(text) == 'object') {
27292         text = text.text;
27293     }
27294     var s = document.createElement("span");
27295     s.className = "ytb-text";
27296     s.innerHTML = text;
27297     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
27298 };
27299 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
27300     enable:Roo.emptyFn,
27301     disable:Roo.emptyFn,
27302     focus:Roo.emptyFn
27303 });
27304
27305 /**
27306  * @class Roo.Toolbar.Button
27307  * @extends Roo.Button
27308  * A button that renders into a toolbar.
27309  * @constructor
27310  * Creates a new Button
27311  * @param {Object} config A standard {@link Roo.Button} config object
27312  */
27313 Roo.Toolbar.Button = function(config){
27314     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
27315 };
27316 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
27317     render : function(td){
27318         this.td = td;
27319         Roo.Toolbar.Button.superclass.render.call(this, td);
27320     },
27321     
27322     /**
27323      * Removes and destroys this button
27324      */
27325     destroy : function(){
27326         Roo.Toolbar.Button.superclass.destroy.call(this);
27327         this.td.parentNode.removeChild(this.td);
27328     },
27329     
27330     /**
27331      * Shows this button
27332      */
27333     show: function(){
27334         this.hidden = false;
27335         this.td.style.display = "";
27336     },
27337     
27338     /**
27339      * Hides this button
27340      */
27341     hide: function(){
27342         this.hidden = true;
27343         this.td.style.display = "none";
27344     },
27345
27346     /**
27347      * Disables this item
27348      */
27349     disable : function(){
27350         Roo.fly(this.td).addClass("x-item-disabled");
27351         this.disabled = true;
27352     },
27353
27354     /**
27355      * Enables this item
27356      */
27357     enable : function(){
27358         Roo.fly(this.td).removeClass("x-item-disabled");
27359         this.disabled = false;
27360     }
27361 });
27362 // backwards compat
27363 Roo.ToolbarButton = Roo.Toolbar.Button;
27364
27365 /**
27366  * @class Roo.Toolbar.SplitButton
27367  * @extends Roo.SplitButton
27368  * A menu button that renders into a toolbar.
27369  * @constructor
27370  * Creates a new SplitButton
27371  * @param {Object} config A standard {@link Roo.SplitButton} config object
27372  */
27373 Roo.Toolbar.SplitButton = function(config){
27374     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
27375 };
27376 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
27377     render : function(td){
27378         this.td = td;
27379         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
27380     },
27381     
27382     /**
27383      * Removes and destroys this button
27384      */
27385     destroy : function(){
27386         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
27387         this.td.parentNode.removeChild(this.td);
27388     },
27389     
27390     /**
27391      * Shows this button
27392      */
27393     show: function(){
27394         this.hidden = false;
27395         this.td.style.display = "";
27396     },
27397     
27398     /**
27399      * Hides this button
27400      */
27401     hide: function(){
27402         this.hidden = true;
27403         this.td.style.display = "none";
27404     }
27405 });
27406
27407 // backwards compat
27408 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
27409  * Based on:
27410  * Ext JS Library 1.1.1
27411  * Copyright(c) 2006-2007, Ext JS, LLC.
27412  *
27413  * Originally Released Under LGPL - original licence link has changed is not relivant.
27414  *
27415  * Fork - LGPL
27416  * <script type="text/javascript">
27417  */
27418  
27419 /**
27420  * @class Roo.PagingToolbar
27421  * @extends Roo.Toolbar
27422  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27423  * @constructor
27424  * Create a new PagingToolbar
27425  * @param {Object} config The config object
27426  */
27427 Roo.PagingToolbar = function(el, ds, config)
27428 {
27429     // old args format still supported... - xtype is prefered..
27430     if (typeof(el) == 'object' && el.xtype) {
27431         // created from xtype...
27432         config = el;
27433         ds = el.dataSource;
27434         el = config.container;
27435     }
27436     var items = [];
27437     if (config.items) {
27438         items = config.items;
27439         config.items = [];
27440     }
27441     
27442     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
27443     this.ds = ds;
27444     this.cursor = 0;
27445     this.renderButtons(this.el);
27446     this.bind(ds);
27447     
27448     // supprot items array.
27449    
27450     Roo.each(items, function(e) {
27451         this.add(Roo.factory(e));
27452     },this);
27453     
27454 };
27455
27456 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
27457     /**
27458      * @cfg {Roo.data.Store} dataSource
27459      * The underlying data store providing the paged data
27460      */
27461     /**
27462      * @cfg {String/HTMLElement/Element} container
27463      * container The id or element that will contain the toolbar
27464      */
27465     /**
27466      * @cfg {Boolean} displayInfo
27467      * True to display the displayMsg (defaults to false)
27468      */
27469     /**
27470      * @cfg {Number} pageSize
27471      * The number of records to display per page (defaults to 20)
27472      */
27473     pageSize: 20,
27474     /**
27475      * @cfg {String} displayMsg
27476      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27477      */
27478     displayMsg : 'Displaying {0} - {1} of {2}',
27479     /**
27480      * @cfg {String} emptyMsg
27481      * The message to display when no records are found (defaults to "No data to display")
27482      */
27483     emptyMsg : 'No data to display',
27484     /**
27485      * Customizable piece of the default paging text (defaults to "Page")
27486      * @type String
27487      */
27488     beforePageText : "Page",
27489     /**
27490      * Customizable piece of the default paging text (defaults to "of %0")
27491      * @type String
27492      */
27493     afterPageText : "of {0}",
27494     /**
27495      * Customizable piece of the default paging text (defaults to "First Page")
27496      * @type String
27497      */
27498     firstText : "First Page",
27499     /**
27500      * Customizable piece of the default paging text (defaults to "Previous Page")
27501      * @type String
27502      */
27503     prevText : "Previous Page",
27504     /**
27505      * Customizable piece of the default paging text (defaults to "Next Page")
27506      * @type String
27507      */
27508     nextText : "Next Page",
27509     /**
27510      * Customizable piece of the default paging text (defaults to "Last Page")
27511      * @type String
27512      */
27513     lastText : "Last Page",
27514     /**
27515      * Customizable piece of the default paging text (defaults to "Refresh")
27516      * @type String
27517      */
27518     refreshText : "Refresh",
27519
27520     // private
27521     renderButtons : function(el){
27522         Roo.PagingToolbar.superclass.render.call(this, el);
27523         this.first = this.addButton({
27524             tooltip: this.firstText,
27525             cls: "x-btn-icon x-grid-page-first",
27526             disabled: true,
27527             handler: this.onClick.createDelegate(this, ["first"])
27528         });
27529         this.prev = this.addButton({
27530             tooltip: this.prevText,
27531             cls: "x-btn-icon x-grid-page-prev",
27532             disabled: true,
27533             handler: this.onClick.createDelegate(this, ["prev"])
27534         });
27535         //this.addSeparator();
27536         this.add(this.beforePageText);
27537         this.field = Roo.get(this.addDom({
27538            tag: "input",
27539            type: "text",
27540            size: "3",
27541            value: "1",
27542            cls: "x-grid-page-number"
27543         }).el);
27544         this.field.on("keydown", this.onPagingKeydown, this);
27545         this.field.on("focus", function(){this.dom.select();});
27546         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
27547         this.field.setHeight(18);
27548         //this.addSeparator();
27549         this.next = this.addButton({
27550             tooltip: this.nextText,
27551             cls: "x-btn-icon x-grid-page-next",
27552             disabled: true,
27553             handler: this.onClick.createDelegate(this, ["next"])
27554         });
27555         this.last = this.addButton({
27556             tooltip: this.lastText,
27557             cls: "x-btn-icon x-grid-page-last",
27558             disabled: true,
27559             handler: this.onClick.createDelegate(this, ["last"])
27560         });
27561         //this.addSeparator();
27562         this.loading = this.addButton({
27563             tooltip: this.refreshText,
27564             cls: "x-btn-icon x-grid-loading",
27565             handler: this.onClick.createDelegate(this, ["refresh"])
27566         });
27567
27568         if(this.displayInfo){
27569             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
27570         }
27571     },
27572
27573     // private
27574     updateInfo : function(){
27575         if(this.displayEl){
27576             var count = this.ds.getCount();
27577             var msg = count == 0 ?
27578                 this.emptyMsg :
27579                 String.format(
27580                     this.displayMsg,
27581                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27582                 );
27583             this.displayEl.update(msg);
27584         }
27585     },
27586
27587     // private
27588     onLoad : function(ds, r, o){
27589        this.cursor = o.params ? o.params.start : 0;
27590        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
27591
27592        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
27593        this.field.dom.value = ap;
27594        this.first.setDisabled(ap == 1);
27595        this.prev.setDisabled(ap == 1);
27596        this.next.setDisabled(ap == ps);
27597        this.last.setDisabled(ap == ps);
27598        this.loading.enable();
27599        this.updateInfo();
27600     },
27601
27602     // private
27603     getPageData : function(){
27604         var total = this.ds.getTotalCount();
27605         return {
27606             total : total,
27607             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27608             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27609         };
27610     },
27611
27612     // private
27613     onLoadError : function(){
27614         this.loading.enable();
27615     },
27616
27617     // private
27618     onPagingKeydown : function(e){
27619         var k = e.getKey();
27620         var d = this.getPageData();
27621         if(k == e.RETURN){
27622             var v = this.field.dom.value, pageNum;
27623             if(!v || isNaN(pageNum = parseInt(v, 10))){
27624                 this.field.dom.value = d.activePage;
27625                 return;
27626             }
27627             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27628             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27629             e.stopEvent();
27630         }
27631         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))
27632         {
27633           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27634           this.field.dom.value = pageNum;
27635           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27636           e.stopEvent();
27637         }
27638         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27639         {
27640           var v = this.field.dom.value, pageNum; 
27641           var increment = (e.shiftKey) ? 10 : 1;
27642           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27643             increment *= -1;
27644           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27645             this.field.dom.value = d.activePage;
27646             return;
27647           }
27648           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27649           {
27650             this.field.dom.value = parseInt(v, 10) + increment;
27651             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27652             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27653           }
27654           e.stopEvent();
27655         }
27656     },
27657
27658     // private
27659     beforeLoad : function(){
27660         if(this.loading){
27661             this.loading.disable();
27662         }
27663     },
27664
27665     // private
27666     onClick : function(which){
27667         var ds = this.ds;
27668         switch(which){
27669             case "first":
27670                 ds.load({params:{start: 0, limit: this.pageSize}});
27671             break;
27672             case "prev":
27673                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27674             break;
27675             case "next":
27676                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27677             break;
27678             case "last":
27679                 var total = ds.getTotalCount();
27680                 var extra = total % this.pageSize;
27681                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27682                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27683             break;
27684             case "refresh":
27685                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27686             break;
27687         }
27688     },
27689
27690     /**
27691      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27692      * @param {Roo.data.Store} store The data store to unbind
27693      */
27694     unbind : function(ds){
27695         ds.un("beforeload", this.beforeLoad, this);
27696         ds.un("load", this.onLoad, this);
27697         ds.un("loadexception", this.onLoadError, this);
27698         ds.un("remove", this.updateInfo, this);
27699         ds.un("add", this.updateInfo, this);
27700         this.ds = undefined;
27701     },
27702
27703     /**
27704      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27705      * @param {Roo.data.Store} store The data store to bind
27706      */
27707     bind : function(ds){
27708         ds.on("beforeload", this.beforeLoad, this);
27709         ds.on("load", this.onLoad, this);
27710         ds.on("loadexception", this.onLoadError, this);
27711         ds.on("remove", this.updateInfo, this);
27712         ds.on("add", this.updateInfo, this);
27713         this.ds = ds;
27714     }
27715 });/*
27716  * Based on:
27717  * Ext JS Library 1.1.1
27718  * Copyright(c) 2006-2007, Ext JS, LLC.
27719  *
27720  * Originally Released Under LGPL - original licence link has changed is not relivant.
27721  *
27722  * Fork - LGPL
27723  * <script type="text/javascript">
27724  */
27725
27726 /**
27727  * @class Roo.Resizable
27728  * @extends Roo.util.Observable
27729  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
27730  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
27731  * 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
27732  * the element will be wrapped for you automatically.</p>
27733  * <p>Here is the list of valid resize handles:</p>
27734  * <pre>
27735 Value   Description
27736 ------  -------------------
27737  'n'     north
27738  's'     south
27739  'e'     east
27740  'w'     west
27741  'nw'    northwest
27742  'sw'    southwest
27743  'se'    southeast
27744  'ne'    northeast
27745  'hd'    horizontal drag
27746  'all'   all
27747 </pre>
27748  * <p>Here's an example showing the creation of a typical Resizable:</p>
27749  * <pre><code>
27750 var resizer = new Roo.Resizable("element-id", {
27751     handles: 'all',
27752     minWidth: 200,
27753     minHeight: 100,
27754     maxWidth: 500,
27755     maxHeight: 400,
27756     pinned: true
27757 });
27758 resizer.on("resize", myHandler);
27759 </code></pre>
27760  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
27761  * resizer.east.setDisplayed(false);</p>
27762  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
27763  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
27764  * resize operation's new size (defaults to [0, 0])
27765  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
27766  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
27767  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
27768  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
27769  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
27770  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
27771  * @cfg {Number} width The width of the element in pixels (defaults to null)
27772  * @cfg {Number} height The height of the element in pixels (defaults to null)
27773  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
27774  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
27775  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
27776  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
27777  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
27778  * in favor of the handles config option (defaults to false)
27779  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
27780  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
27781  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
27782  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
27783  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
27784  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
27785  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
27786  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
27787  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
27788  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
27789  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
27790  * @constructor
27791  * Create a new resizable component
27792  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
27793  * @param {Object} config configuration options
27794   */
27795 Roo.Resizable = function(el, config)
27796 {
27797     this.el = Roo.get(el);
27798
27799     if(config && config.wrap){
27800         config.resizeChild = this.el;
27801         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
27802         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
27803         this.el.setStyle("overflow", "hidden");
27804         this.el.setPositioning(config.resizeChild.getPositioning());
27805         config.resizeChild.clearPositioning();
27806         if(!config.width || !config.height){
27807             var csize = config.resizeChild.getSize();
27808             this.el.setSize(csize.width, csize.height);
27809         }
27810         if(config.pinned && !config.adjustments){
27811             config.adjustments = "auto";
27812         }
27813     }
27814
27815     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
27816     this.proxy.unselectable();
27817     this.proxy.enableDisplayMode('block');
27818
27819     Roo.apply(this, config);
27820
27821     if(this.pinned){
27822         this.disableTrackOver = true;
27823         this.el.addClass("x-resizable-pinned");
27824     }
27825     // if the element isn't positioned, make it relative
27826     var position = this.el.getStyle("position");
27827     if(position != "absolute" && position != "fixed"){
27828         this.el.setStyle("position", "relative");
27829     }
27830     if(!this.handles){ // no handles passed, must be legacy style
27831         this.handles = 's,e,se';
27832         if(this.multiDirectional){
27833             this.handles += ',n,w';
27834         }
27835     }
27836     if(this.handles == "all"){
27837         this.handles = "n s e w ne nw se sw";
27838     }
27839     var hs = this.handles.split(/\s*?[,;]\s*?| /);
27840     var ps = Roo.Resizable.positions;
27841     for(var i = 0, len = hs.length; i < len; i++){
27842         if(hs[i] && ps[hs[i]]){
27843             var pos = ps[hs[i]];
27844             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
27845         }
27846     }
27847     // legacy
27848     this.corner = this.southeast;
27849     
27850     // updateBox = the box can move..
27851     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
27852         this.updateBox = true;
27853     }
27854
27855     this.activeHandle = null;
27856
27857     if(this.resizeChild){
27858         if(typeof this.resizeChild == "boolean"){
27859             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
27860         }else{
27861             this.resizeChild = Roo.get(this.resizeChild, true);
27862         }
27863     }
27864     
27865     if(this.adjustments == "auto"){
27866         var rc = this.resizeChild;
27867         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
27868         if(rc && (hw || hn)){
27869             rc.position("relative");
27870             rc.setLeft(hw ? hw.el.getWidth() : 0);
27871             rc.setTop(hn ? hn.el.getHeight() : 0);
27872         }
27873         this.adjustments = [
27874             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
27875             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
27876         ];
27877     }
27878
27879     if(this.draggable){
27880         this.dd = this.dynamic ?
27881             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
27882         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
27883     }
27884
27885     // public events
27886     this.addEvents({
27887         /**
27888          * @event beforeresize
27889          * Fired before resize is allowed. Set enabled to false to cancel resize.
27890          * @param {Roo.Resizable} this
27891          * @param {Roo.EventObject} e The mousedown event
27892          */
27893         "beforeresize" : true,
27894         /**
27895          * @event resize
27896          * Fired after a resize.
27897          * @param {Roo.Resizable} this
27898          * @param {Number} width The new width
27899          * @param {Number} height The new height
27900          * @param {Roo.EventObject} e The mouseup event
27901          */
27902         "resize" : true
27903     });
27904
27905     if(this.width !== null && this.height !== null){
27906         this.resizeTo(this.width, this.height);
27907     }else{
27908         this.updateChildSize();
27909     }
27910     if(Roo.isIE){
27911         this.el.dom.style.zoom = 1;
27912     }
27913     Roo.Resizable.superclass.constructor.call(this);
27914 };
27915
27916 Roo.extend(Roo.Resizable, Roo.util.Observable, {
27917         resizeChild : false,
27918         adjustments : [0, 0],
27919         minWidth : 5,
27920         minHeight : 5,
27921         maxWidth : 10000,
27922         maxHeight : 10000,
27923         enabled : true,
27924         animate : false,
27925         duration : .35,
27926         dynamic : false,
27927         handles : false,
27928         multiDirectional : false,
27929         disableTrackOver : false,
27930         easing : 'easeOutStrong',
27931         widthIncrement : 0,
27932         heightIncrement : 0,
27933         pinned : false,
27934         width : null,
27935         height : null,
27936         preserveRatio : false,
27937         transparent: false,
27938         minX: 0,
27939         minY: 0,
27940         draggable: false,
27941
27942         /**
27943          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
27944          */
27945         constrainTo: undefined,
27946         /**
27947          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
27948          */
27949         resizeRegion: undefined,
27950
27951
27952     /**
27953      * Perform a manual resize
27954      * @param {Number} width
27955      * @param {Number} height
27956      */
27957     resizeTo : function(width, height){
27958         this.el.setSize(width, height);
27959         this.updateChildSize();
27960         this.fireEvent("resize", this, width, height, null);
27961     },
27962
27963     // private
27964     startSizing : function(e, handle){
27965         this.fireEvent("beforeresize", this, e);
27966         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
27967
27968             if(!this.overlay){
27969                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
27970                 this.overlay.unselectable();
27971                 this.overlay.enableDisplayMode("block");
27972                 this.overlay.on("mousemove", this.onMouseMove, this);
27973                 this.overlay.on("mouseup", this.onMouseUp, this);
27974             }
27975             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
27976
27977             this.resizing = true;
27978             this.startBox = this.el.getBox();
27979             this.startPoint = e.getXY();
27980             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
27981                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
27982
27983             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
27984             this.overlay.show();
27985
27986             if(this.constrainTo) {
27987                 var ct = Roo.get(this.constrainTo);
27988                 this.resizeRegion = ct.getRegion().adjust(
27989                     ct.getFrameWidth('t'),
27990                     ct.getFrameWidth('l'),
27991                     -ct.getFrameWidth('b'),
27992                     -ct.getFrameWidth('r')
27993                 );
27994             }
27995
27996             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
27997             this.proxy.show();
27998             this.proxy.setBox(this.startBox);
27999             if(!this.dynamic){
28000                 this.proxy.setStyle('visibility', 'visible');
28001             }
28002         }
28003     },
28004
28005     // private
28006     onMouseDown : function(handle, e){
28007         if(this.enabled){
28008             e.stopEvent();
28009             this.activeHandle = handle;
28010             this.startSizing(e, handle);
28011         }
28012     },
28013
28014     // private
28015     onMouseUp : function(e){
28016         var size = this.resizeElement();
28017         this.resizing = false;
28018         this.handleOut();
28019         this.overlay.hide();
28020         this.proxy.hide();
28021         this.fireEvent("resize", this, size.width, size.height, e);
28022     },
28023
28024     // private
28025     updateChildSize : function(){
28026         if(this.resizeChild){
28027             var el = this.el;
28028             var child = this.resizeChild;
28029             var adj = this.adjustments;
28030             if(el.dom.offsetWidth){
28031                 var b = el.getSize(true);
28032                 child.setSize(b.width+adj[0], b.height+adj[1]);
28033             }
28034             // Second call here for IE
28035             // The first call enables instant resizing and
28036             // the second call corrects scroll bars if they
28037             // exist
28038             if(Roo.isIE){
28039                 setTimeout(function(){
28040                     if(el.dom.offsetWidth){
28041                         var b = el.getSize(true);
28042                         child.setSize(b.width+adj[0], b.height+adj[1]);
28043                     }
28044                 }, 10);
28045             }
28046         }
28047     },
28048
28049     // private
28050     snap : function(value, inc, min){
28051         if(!inc || !value) return value;
28052         var newValue = value;
28053         var m = value % inc;
28054         if(m > 0){
28055             if(m > (inc/2)){
28056                 newValue = value + (inc-m);
28057             }else{
28058                 newValue = value - m;
28059             }
28060         }
28061         return Math.max(min, newValue);
28062     },
28063
28064     // private
28065     resizeElement : function(){
28066         var box = this.proxy.getBox();
28067         if(this.updateBox){
28068             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
28069         }else{
28070             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
28071         }
28072         this.updateChildSize();
28073         if(!this.dynamic){
28074             this.proxy.hide();
28075         }
28076         return box;
28077     },
28078
28079     // private
28080     constrain : function(v, diff, m, mx){
28081         if(v - diff < m){
28082             diff = v - m;
28083         }else if(v - diff > mx){
28084             diff = mx - v;
28085         }
28086         return diff;
28087     },
28088
28089     // private
28090     onMouseMove : function(e){
28091         if(this.enabled){
28092             try{// try catch so if something goes wrong the user doesn't get hung
28093
28094             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
28095                 return;
28096             }
28097
28098             //var curXY = this.startPoint;
28099             var curSize = this.curSize || this.startBox;
28100             var x = this.startBox.x, y = this.startBox.y;
28101             var ox = x, oy = y;
28102             var w = curSize.width, h = curSize.height;
28103             var ow = w, oh = h;
28104             var mw = this.minWidth, mh = this.minHeight;
28105             var mxw = this.maxWidth, mxh = this.maxHeight;
28106             var wi = this.widthIncrement;
28107             var hi = this.heightIncrement;
28108
28109             var eventXY = e.getXY();
28110             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
28111             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
28112
28113             var pos = this.activeHandle.position;
28114
28115             switch(pos){
28116                 case "east":
28117                     w += diffX;
28118                     w = Math.min(Math.max(mw, w), mxw);
28119                     break;
28120              
28121                 case "south":
28122                     h += diffY;
28123                     h = Math.min(Math.max(mh, h), mxh);
28124                     break;
28125                 case "southeast":
28126                     w += diffX;
28127                     h += diffY;
28128                     w = Math.min(Math.max(mw, w), mxw);
28129                     h = Math.min(Math.max(mh, h), mxh);
28130                     break;
28131                 case "north":
28132                     diffY = this.constrain(h, diffY, mh, mxh);
28133                     y += diffY;
28134                     h -= diffY;
28135                     break;
28136                 case "hdrag":
28137                     
28138                     if (wi) {
28139                         var adiffX = Math.abs(diffX);
28140                         var sub = (adiffX % wi); // how much 
28141                         if (sub > (wi/2)) { // far enough to snap
28142                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
28143                         } else {
28144                             // remove difference.. 
28145                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
28146                         }
28147                     }
28148                     x += diffX;
28149                     x = Math.max(this.minX, x);
28150                     break;
28151                 case "west":
28152                     diffX = this.constrain(w, diffX, mw, mxw);
28153                     x += diffX;
28154                     w -= diffX;
28155                     break;
28156                 case "northeast":
28157                     w += diffX;
28158                     w = Math.min(Math.max(mw, w), mxw);
28159                     diffY = this.constrain(h, diffY, mh, mxh);
28160                     y += diffY;
28161                     h -= diffY;
28162                     break;
28163                 case "northwest":
28164                     diffX = this.constrain(w, diffX, mw, mxw);
28165                     diffY = this.constrain(h, diffY, mh, mxh);
28166                     y += diffY;
28167                     h -= diffY;
28168                     x += diffX;
28169                     w -= diffX;
28170                     break;
28171                case "southwest":
28172                     diffX = this.constrain(w, diffX, mw, mxw);
28173                     h += diffY;
28174                     h = Math.min(Math.max(mh, h), mxh);
28175                     x += diffX;
28176                     w -= diffX;
28177                     break;
28178             }
28179
28180             var sw = this.snap(w, wi, mw);
28181             var sh = this.snap(h, hi, mh);
28182             if(sw != w || sh != h){
28183                 switch(pos){
28184                     case "northeast":
28185                         y -= sh - h;
28186                     break;
28187                     case "north":
28188                         y -= sh - h;
28189                         break;
28190                     case "southwest":
28191                         x -= sw - w;
28192                     break;
28193                     case "west":
28194                         x -= sw - w;
28195                         break;
28196                     case "northwest":
28197                         x -= sw - w;
28198                         y -= sh - h;
28199                     break;
28200                 }
28201                 w = sw;
28202                 h = sh;
28203             }
28204
28205             if(this.preserveRatio){
28206                 switch(pos){
28207                     case "southeast":
28208                     case "east":
28209                         h = oh * (w/ow);
28210                         h = Math.min(Math.max(mh, h), mxh);
28211                         w = ow * (h/oh);
28212                        break;
28213                     case "south":
28214                         w = ow * (h/oh);
28215                         w = Math.min(Math.max(mw, w), mxw);
28216                         h = oh * (w/ow);
28217                         break;
28218                     case "northeast":
28219                         w = ow * (h/oh);
28220                         w = Math.min(Math.max(mw, w), mxw);
28221                         h = oh * (w/ow);
28222                     break;
28223                     case "north":
28224                         var tw = w;
28225                         w = ow * (h/oh);
28226                         w = Math.min(Math.max(mw, w), mxw);
28227                         h = oh * (w/ow);
28228                         x += (tw - w) / 2;
28229                         break;
28230                     case "southwest":
28231                         h = oh * (w/ow);
28232                         h = Math.min(Math.max(mh, h), mxh);
28233                         var tw = w;
28234                         w = ow * (h/oh);
28235                         x += tw - w;
28236                         break;
28237                     case "west":
28238                         var th = h;
28239                         h = oh * (w/ow);
28240                         h = Math.min(Math.max(mh, h), mxh);
28241                         y += (th - h) / 2;
28242                         var tw = w;
28243                         w = ow * (h/oh);
28244                         x += tw - w;
28245                        break;
28246                     case "northwest":
28247                         var tw = w;
28248                         var th = h;
28249                         h = oh * (w/ow);
28250                         h = Math.min(Math.max(mh, h), mxh);
28251                         w = ow * (h/oh);
28252                         y += th - h;
28253                         x += tw - w;
28254                        break;
28255
28256                 }
28257             }
28258             if (pos == 'hdrag') {
28259                 w = ow;
28260             }
28261             this.proxy.setBounds(x, y, w, h);
28262             if(this.dynamic){
28263                 this.resizeElement();
28264             }
28265             }catch(e){}
28266         }
28267     },
28268
28269     // private
28270     handleOver : function(){
28271         if(this.enabled){
28272             this.el.addClass("x-resizable-over");
28273         }
28274     },
28275
28276     // private
28277     handleOut : function(){
28278         if(!this.resizing){
28279             this.el.removeClass("x-resizable-over");
28280         }
28281     },
28282
28283     /**
28284      * Returns the element this component is bound to.
28285      * @return {Roo.Element}
28286      */
28287     getEl : function(){
28288         return this.el;
28289     },
28290
28291     /**
28292      * Returns the resizeChild element (or null).
28293      * @return {Roo.Element}
28294      */
28295     getResizeChild : function(){
28296         return this.resizeChild;
28297     },
28298
28299     /**
28300      * Destroys this resizable. If the element was wrapped and
28301      * removeEl is not true then the element remains.
28302      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
28303      */
28304     destroy : function(removeEl){
28305         this.proxy.remove();
28306         if(this.overlay){
28307             this.overlay.removeAllListeners();
28308             this.overlay.remove();
28309         }
28310         var ps = Roo.Resizable.positions;
28311         for(var k in ps){
28312             if(typeof ps[k] != "function" && this[ps[k]]){
28313                 var h = this[ps[k]];
28314                 h.el.removeAllListeners();
28315                 h.el.remove();
28316             }
28317         }
28318         if(removeEl){
28319             this.el.update("");
28320             this.el.remove();
28321         }
28322     }
28323 });
28324
28325 // private
28326 // hash to map config positions to true positions
28327 Roo.Resizable.positions = {
28328     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
28329     hd: "hdrag"
28330 };
28331
28332 // private
28333 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
28334     if(!this.tpl){
28335         // only initialize the template if resizable is used
28336         var tpl = Roo.DomHelper.createTemplate(
28337             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
28338         );
28339         tpl.compile();
28340         Roo.Resizable.Handle.prototype.tpl = tpl;
28341     }
28342     this.position = pos;
28343     this.rz = rz;
28344     // show north drag fro topdra
28345     var handlepos = pos == 'hdrag' ? 'north' : pos;
28346     
28347     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
28348     if (pos == 'hdrag') {
28349         this.el.setStyle('cursor', 'pointer');
28350     }
28351     this.el.unselectable();
28352     if(transparent){
28353         this.el.setOpacity(0);
28354     }
28355     this.el.on("mousedown", this.onMouseDown, this);
28356     if(!disableTrackOver){
28357         this.el.on("mouseover", this.onMouseOver, this);
28358         this.el.on("mouseout", this.onMouseOut, this);
28359     }
28360 };
28361
28362 // private
28363 Roo.Resizable.Handle.prototype = {
28364     afterResize : function(rz){
28365         // do nothing
28366     },
28367     // private
28368     onMouseDown : function(e){
28369         this.rz.onMouseDown(this, e);
28370     },
28371     // private
28372     onMouseOver : function(e){
28373         this.rz.handleOver(this, e);
28374     },
28375     // private
28376     onMouseOut : function(e){
28377         this.rz.handleOut(this, e);
28378     }
28379 };/*
28380  * Based on:
28381  * Ext JS Library 1.1.1
28382  * Copyright(c) 2006-2007, Ext JS, LLC.
28383  *
28384  * Originally Released Under LGPL - original licence link has changed is not relivant.
28385  *
28386  * Fork - LGPL
28387  * <script type="text/javascript">
28388  */
28389
28390 /**
28391  * @class Roo.Editor
28392  * @extends Roo.Component
28393  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
28394  * @constructor
28395  * Create a new Editor
28396  * @param {Roo.form.Field} field The Field object (or descendant)
28397  * @param {Object} config The config object
28398  */
28399 Roo.Editor = function(field, config){
28400     Roo.Editor.superclass.constructor.call(this, config);
28401     this.field = field;
28402     this.addEvents({
28403         /**
28404              * @event beforestartedit
28405              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
28406              * false from the handler of this event.
28407              * @param {Editor} this
28408              * @param {Roo.Element} boundEl The underlying element bound to this editor
28409              * @param {Mixed} value The field value being set
28410              */
28411         "beforestartedit" : true,
28412         /**
28413              * @event startedit
28414              * Fires when this editor is displayed
28415              * @param {Roo.Element} boundEl The underlying element bound to this editor
28416              * @param {Mixed} value The starting field value
28417              */
28418         "startedit" : true,
28419         /**
28420              * @event beforecomplete
28421              * Fires after a change has been made to the field, but before the change is reflected in the underlying
28422              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
28423              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
28424              * event will not fire since no edit actually occurred.
28425              * @param {Editor} this
28426              * @param {Mixed} value The current field value
28427              * @param {Mixed} startValue The original field value
28428              */
28429         "beforecomplete" : true,
28430         /**
28431              * @event complete
28432              * Fires after editing is complete and any changed value has been written to the underlying field.
28433              * @param {Editor} this
28434              * @param {Mixed} value The current field value
28435              * @param {Mixed} startValue The original field value
28436              */
28437         "complete" : true,
28438         /**
28439          * @event specialkey
28440          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
28441          * {@link Roo.EventObject#getKey} to determine which key was pressed.
28442          * @param {Roo.form.Field} this
28443          * @param {Roo.EventObject} e The event object
28444          */
28445         "specialkey" : true
28446     });
28447 };
28448
28449 Roo.extend(Roo.Editor, Roo.Component, {
28450     /**
28451      * @cfg {Boolean/String} autosize
28452      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
28453      * or "height" to adopt the height only (defaults to false)
28454      */
28455     /**
28456      * @cfg {Boolean} revertInvalid
28457      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
28458      * validation fails (defaults to true)
28459      */
28460     /**
28461      * @cfg {Boolean} ignoreNoChange
28462      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
28463      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
28464      * will never be ignored.
28465      */
28466     /**
28467      * @cfg {Boolean} hideEl
28468      * False to keep the bound element visible while the editor is displayed (defaults to true)
28469      */
28470     /**
28471      * @cfg {Mixed} value
28472      * The data value of the underlying field (defaults to "")
28473      */
28474     value : "",
28475     /**
28476      * @cfg {String} alignment
28477      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
28478      */
28479     alignment: "c-c?",
28480     /**
28481      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
28482      * for bottom-right shadow (defaults to "frame")
28483      */
28484     shadow : "frame",
28485     /**
28486      * @cfg {Boolean} constrain True to constrain the editor to the viewport
28487      */
28488     constrain : false,
28489     /**
28490      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
28491      */
28492     completeOnEnter : false,
28493     /**
28494      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
28495      */
28496     cancelOnEsc : false,
28497     /**
28498      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
28499      */
28500     updateEl : false,
28501
28502     // private
28503     onRender : function(ct, position){
28504         this.el = new Roo.Layer({
28505             shadow: this.shadow,
28506             cls: "x-editor",
28507             parentEl : ct,
28508             shim : this.shim,
28509             shadowOffset:4,
28510             id: this.id,
28511             constrain: this.constrain
28512         });
28513         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
28514         if(this.field.msgTarget != 'title'){
28515             this.field.msgTarget = 'qtip';
28516         }
28517         this.field.render(this.el);
28518         if(Roo.isGecko){
28519             this.field.el.dom.setAttribute('autocomplete', 'off');
28520         }
28521         this.field.on("specialkey", this.onSpecialKey, this);
28522         if(this.swallowKeys){
28523             this.field.el.swallowEvent(['keydown','keypress']);
28524         }
28525         this.field.show();
28526         this.field.on("blur", this.onBlur, this);
28527         if(this.field.grow){
28528             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
28529         }
28530     },
28531
28532     onSpecialKey : function(field, e)
28533     {
28534         //Roo.log('editor onSpecialKey');
28535         if(this.completeOnEnter && e.getKey() == e.ENTER){
28536             e.stopEvent();
28537             this.completeEdit();
28538             return;
28539         }
28540         // do not fire special key otherwise it might hide close the editor...
28541         if(e.getKey() == e.ENTER){    
28542             return;
28543         }
28544         if(this.cancelOnEsc && e.getKey() == e.ESC){
28545             this.cancelEdit();
28546             return;
28547         } 
28548         this.fireEvent('specialkey', field, e);
28549     
28550     },
28551
28552     /**
28553      * Starts the editing process and shows the editor.
28554      * @param {String/HTMLElement/Element} el The element to edit
28555      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
28556       * to the innerHTML of el.
28557      */
28558     startEdit : function(el, value){
28559         if(this.editing){
28560             this.completeEdit();
28561         }
28562         this.boundEl = Roo.get(el);
28563         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
28564         if(!this.rendered){
28565             this.render(this.parentEl || document.body);
28566         }
28567         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
28568             return;
28569         }
28570         this.startValue = v;
28571         this.field.setValue(v);
28572         if(this.autoSize){
28573             var sz = this.boundEl.getSize();
28574             switch(this.autoSize){
28575                 case "width":
28576                 this.setSize(sz.width,  "");
28577                 break;
28578                 case "height":
28579                 this.setSize("",  sz.height);
28580                 break;
28581                 default:
28582                 this.setSize(sz.width,  sz.height);
28583             }
28584         }
28585         this.el.alignTo(this.boundEl, this.alignment);
28586         this.editing = true;
28587         if(Roo.QuickTips){
28588             Roo.QuickTips.disable();
28589         }
28590         this.show();
28591     },
28592
28593     /**
28594      * Sets the height and width of this editor.
28595      * @param {Number} width The new width
28596      * @param {Number} height The new height
28597      */
28598     setSize : function(w, h){
28599         this.field.setSize(w, h);
28600         if(this.el){
28601             this.el.sync();
28602         }
28603     },
28604
28605     /**
28606      * Realigns the editor to the bound field based on the current alignment config value.
28607      */
28608     realign : function(){
28609         this.el.alignTo(this.boundEl, this.alignment);
28610     },
28611
28612     /**
28613      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
28614      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
28615      */
28616     completeEdit : function(remainVisible){
28617         if(!this.editing){
28618             return;
28619         }
28620         var v = this.getValue();
28621         if(this.revertInvalid !== false && !this.field.isValid()){
28622             v = this.startValue;
28623             this.cancelEdit(true);
28624         }
28625         if(String(v) === String(this.startValue) && this.ignoreNoChange){
28626             this.editing = false;
28627             this.hide();
28628             return;
28629         }
28630         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
28631             this.editing = false;
28632             if(this.updateEl && this.boundEl){
28633                 this.boundEl.update(v);
28634             }
28635             if(remainVisible !== true){
28636                 this.hide();
28637             }
28638             this.fireEvent("complete", this, v, this.startValue);
28639         }
28640     },
28641
28642     // private
28643     onShow : function(){
28644         this.el.show();
28645         if(this.hideEl !== false){
28646             this.boundEl.hide();
28647         }
28648         this.field.show();
28649         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
28650             this.fixIEFocus = true;
28651             this.deferredFocus.defer(50, this);
28652         }else{
28653             this.field.focus();
28654         }
28655         this.fireEvent("startedit", this.boundEl, this.startValue);
28656     },
28657
28658     deferredFocus : function(){
28659         if(this.editing){
28660             this.field.focus();
28661         }
28662     },
28663
28664     /**
28665      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
28666      * reverted to the original starting value.
28667      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
28668      * cancel (defaults to false)
28669      */
28670     cancelEdit : function(remainVisible){
28671         if(this.editing){
28672             this.setValue(this.startValue);
28673             if(remainVisible !== true){
28674                 this.hide();
28675             }
28676         }
28677     },
28678
28679     // private
28680     onBlur : function(){
28681         if(this.allowBlur !== true && this.editing){
28682             this.completeEdit();
28683         }
28684     },
28685
28686     // private
28687     onHide : function(){
28688         if(this.editing){
28689             this.completeEdit();
28690             return;
28691         }
28692         this.field.blur();
28693         if(this.field.collapse){
28694             this.field.collapse();
28695         }
28696         this.el.hide();
28697         if(this.hideEl !== false){
28698             this.boundEl.show();
28699         }
28700         if(Roo.QuickTips){
28701             Roo.QuickTips.enable();
28702         }
28703     },
28704
28705     /**
28706      * Sets the data value of the editor
28707      * @param {Mixed} value Any valid value supported by the underlying field
28708      */
28709     setValue : function(v){
28710         this.field.setValue(v);
28711     },
28712
28713     /**
28714      * Gets the data value of the editor
28715      * @return {Mixed} The data value
28716      */
28717     getValue : function(){
28718         return this.field.getValue();
28719     }
28720 });/*
28721  * Based on:
28722  * Ext JS Library 1.1.1
28723  * Copyright(c) 2006-2007, Ext JS, LLC.
28724  *
28725  * Originally Released Under LGPL - original licence link has changed is not relivant.
28726  *
28727  * Fork - LGPL
28728  * <script type="text/javascript">
28729  */
28730  
28731 /**
28732  * @class Roo.BasicDialog
28733  * @extends Roo.util.Observable
28734  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
28735  * <pre><code>
28736 var dlg = new Roo.BasicDialog("my-dlg", {
28737     height: 200,
28738     width: 300,
28739     minHeight: 100,
28740     minWidth: 150,
28741     modal: true,
28742     proxyDrag: true,
28743     shadow: true
28744 });
28745 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
28746 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
28747 dlg.addButton('Cancel', dlg.hide, dlg);
28748 dlg.show();
28749 </code></pre>
28750   <b>A Dialog should always be a direct child of the body element.</b>
28751  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
28752  * @cfg {String} title Default text to display in the title bar (defaults to null)
28753  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
28754  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
28755  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
28756  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
28757  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
28758  * (defaults to null with no animation)
28759  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
28760  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
28761  * property for valid values (defaults to 'all')
28762  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
28763  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
28764  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
28765  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
28766  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
28767  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
28768  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
28769  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
28770  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
28771  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
28772  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
28773  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
28774  * draggable = true (defaults to false)
28775  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
28776  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
28777  * shadow (defaults to false)
28778  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
28779  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
28780  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
28781  * @cfg {Array} buttons Array of buttons
28782  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
28783  * @constructor
28784  * Create a new BasicDialog.
28785  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
28786  * @param {Object} config Configuration options
28787  */
28788 Roo.BasicDialog = function(el, config){
28789     this.el = Roo.get(el);
28790     var dh = Roo.DomHelper;
28791     if(!this.el && config && config.autoCreate){
28792         if(typeof config.autoCreate == "object"){
28793             if(!config.autoCreate.id){
28794                 config.autoCreate.id = el;
28795             }
28796             this.el = dh.append(document.body,
28797                         config.autoCreate, true);
28798         }else{
28799             this.el = dh.append(document.body,
28800                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
28801         }
28802     }
28803     el = this.el;
28804     el.setDisplayed(true);
28805     el.hide = this.hideAction;
28806     this.id = el.id;
28807     el.addClass("x-dlg");
28808
28809     Roo.apply(this, config);
28810
28811     this.proxy = el.createProxy("x-dlg-proxy");
28812     this.proxy.hide = this.hideAction;
28813     this.proxy.setOpacity(.5);
28814     this.proxy.hide();
28815
28816     if(config.width){
28817         el.setWidth(config.width);
28818     }
28819     if(config.height){
28820         el.setHeight(config.height);
28821     }
28822     this.size = el.getSize();
28823     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
28824         this.xy = [config.x,config.y];
28825     }else{
28826         this.xy = el.getCenterXY(true);
28827     }
28828     /** The header element @type Roo.Element */
28829     this.header = el.child("> .x-dlg-hd");
28830     /** The body element @type Roo.Element */
28831     this.body = el.child("> .x-dlg-bd");
28832     /** The footer element @type Roo.Element */
28833     this.footer = el.child("> .x-dlg-ft");
28834
28835     if(!this.header){
28836         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
28837     }
28838     if(!this.body){
28839         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
28840     }
28841
28842     this.header.unselectable();
28843     if(this.title){
28844         this.header.update(this.title);
28845     }
28846     // this element allows the dialog to be focused for keyboard event
28847     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
28848     this.focusEl.swallowEvent("click", true);
28849
28850     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
28851
28852     // wrap the body and footer for special rendering
28853     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
28854     if(this.footer){
28855         this.bwrap.dom.appendChild(this.footer.dom);
28856     }
28857
28858     this.bg = this.el.createChild({
28859         tag: "div", cls:"x-dlg-bg",
28860         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
28861     });
28862     this.centerBg = this.bg.child("div.x-dlg-bg-center");
28863
28864
28865     if(this.autoScroll !== false && !this.autoTabs){
28866         this.body.setStyle("overflow", "auto");
28867     }
28868
28869     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
28870
28871     if(this.closable !== false){
28872         this.el.addClass("x-dlg-closable");
28873         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
28874         this.close.on("click", this.closeClick, this);
28875         this.close.addClassOnOver("x-dlg-close-over");
28876     }
28877     if(this.collapsible !== false){
28878         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
28879         this.collapseBtn.on("click", this.collapseClick, this);
28880         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
28881         this.header.on("dblclick", this.collapseClick, this);
28882     }
28883     if(this.resizable !== false){
28884         this.el.addClass("x-dlg-resizable");
28885         this.resizer = new Roo.Resizable(el, {
28886             minWidth: this.minWidth || 80,
28887             minHeight:this.minHeight || 80,
28888             handles: this.resizeHandles || "all",
28889             pinned: true
28890         });
28891         this.resizer.on("beforeresize", this.beforeResize, this);
28892         this.resizer.on("resize", this.onResize, this);
28893     }
28894     if(this.draggable !== false){
28895         el.addClass("x-dlg-draggable");
28896         if (!this.proxyDrag) {
28897             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
28898         }
28899         else {
28900             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
28901         }
28902         dd.setHandleElId(this.header.id);
28903         dd.endDrag = this.endMove.createDelegate(this);
28904         dd.startDrag = this.startMove.createDelegate(this);
28905         dd.onDrag = this.onDrag.createDelegate(this);
28906         dd.scroll = false;
28907         this.dd = dd;
28908     }
28909     if(this.modal){
28910         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
28911         this.mask.enableDisplayMode("block");
28912         this.mask.hide();
28913         this.el.addClass("x-dlg-modal");
28914     }
28915     if(this.shadow){
28916         this.shadow = new Roo.Shadow({
28917             mode : typeof this.shadow == "string" ? this.shadow : "sides",
28918             offset : this.shadowOffset
28919         });
28920     }else{
28921         this.shadowOffset = 0;
28922     }
28923     if(Roo.useShims && this.shim !== false){
28924         this.shim = this.el.createShim();
28925         this.shim.hide = this.hideAction;
28926         this.shim.hide();
28927     }else{
28928         this.shim = false;
28929     }
28930     if(this.autoTabs){
28931         this.initTabs();
28932     }
28933     if (this.buttons) { 
28934         var bts= this.buttons;
28935         this.buttons = [];
28936         Roo.each(bts, function(b) {
28937             this.addButton(b);
28938         }, this);
28939     }
28940     
28941     
28942     this.addEvents({
28943         /**
28944          * @event keydown
28945          * Fires when a key is pressed
28946          * @param {Roo.BasicDialog} this
28947          * @param {Roo.EventObject} e
28948          */
28949         "keydown" : true,
28950         /**
28951          * @event move
28952          * Fires when this dialog is moved by the user.
28953          * @param {Roo.BasicDialog} this
28954          * @param {Number} x The new page X
28955          * @param {Number} y The new page Y
28956          */
28957         "move" : true,
28958         /**
28959          * @event resize
28960          * Fires when this dialog is resized by the user.
28961          * @param {Roo.BasicDialog} this
28962          * @param {Number} width The new width
28963          * @param {Number} height The new height
28964          */
28965         "resize" : true,
28966         /**
28967          * @event beforehide
28968          * Fires before this dialog is hidden.
28969          * @param {Roo.BasicDialog} this
28970          */
28971         "beforehide" : true,
28972         /**
28973          * @event hide
28974          * Fires when this dialog is hidden.
28975          * @param {Roo.BasicDialog} this
28976          */
28977         "hide" : true,
28978         /**
28979          * @event beforeshow
28980          * Fires before this dialog is shown.
28981          * @param {Roo.BasicDialog} this
28982          */
28983         "beforeshow" : true,
28984         /**
28985          * @event show
28986          * Fires when this dialog is shown.
28987          * @param {Roo.BasicDialog} this
28988          */
28989         "show" : true
28990     });
28991     el.on("keydown", this.onKeyDown, this);
28992     el.on("mousedown", this.toFront, this);
28993     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
28994     this.el.hide();
28995     Roo.DialogManager.register(this);
28996     Roo.BasicDialog.superclass.constructor.call(this);
28997 };
28998
28999 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
29000     shadowOffset: Roo.isIE ? 6 : 5,
29001     minHeight: 80,
29002     minWidth: 200,
29003     minButtonWidth: 75,
29004     defaultButton: null,
29005     buttonAlign: "right",
29006     tabTag: 'div',
29007     firstShow: true,
29008
29009     /**
29010      * Sets the dialog title text
29011      * @param {String} text The title text to display
29012      * @return {Roo.BasicDialog} this
29013      */
29014     setTitle : function(text){
29015         this.header.update(text);
29016         return this;
29017     },
29018
29019     // private
29020     closeClick : function(){
29021         this.hide();
29022     },
29023
29024     // private
29025     collapseClick : function(){
29026         this[this.collapsed ? "expand" : "collapse"]();
29027     },
29028
29029     /**
29030      * Collapses the dialog to its minimized state (only the title bar is visible).
29031      * Equivalent to the user clicking the collapse dialog button.
29032      */
29033     collapse : function(){
29034         if(!this.collapsed){
29035             this.collapsed = true;
29036             this.el.addClass("x-dlg-collapsed");
29037             this.restoreHeight = this.el.getHeight();
29038             this.resizeTo(this.el.getWidth(), this.header.getHeight());
29039         }
29040     },
29041
29042     /**
29043      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
29044      * clicking the expand dialog button.
29045      */
29046     expand : function(){
29047         if(this.collapsed){
29048             this.collapsed = false;
29049             this.el.removeClass("x-dlg-collapsed");
29050             this.resizeTo(this.el.getWidth(), this.restoreHeight);
29051         }
29052     },
29053
29054     /**
29055      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
29056      * @return {Roo.TabPanel} The tabs component
29057      */
29058     initTabs : function(){
29059         var tabs = this.getTabs();
29060         while(tabs.getTab(0)){
29061             tabs.removeTab(0);
29062         }
29063         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
29064             var dom = el.dom;
29065             tabs.addTab(Roo.id(dom), dom.title);
29066             dom.title = "";
29067         });
29068         tabs.activate(0);
29069         return tabs;
29070     },
29071
29072     // private
29073     beforeResize : function(){
29074         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
29075     },
29076
29077     // private
29078     onResize : function(){
29079         this.refreshSize();
29080         this.syncBodyHeight();
29081         this.adjustAssets();
29082         this.focus();
29083         this.fireEvent("resize", this, this.size.width, this.size.height);
29084     },
29085
29086     // private
29087     onKeyDown : function(e){
29088         if(this.isVisible()){
29089             this.fireEvent("keydown", this, e);
29090         }
29091     },
29092
29093     /**
29094      * Resizes the dialog.
29095      * @param {Number} width
29096      * @param {Number} height
29097      * @return {Roo.BasicDialog} this
29098      */
29099     resizeTo : function(width, height){
29100         this.el.setSize(width, height);
29101         this.size = {width: width, height: height};
29102         this.syncBodyHeight();
29103         if(this.fixedcenter){
29104             this.center();
29105         }
29106         if(this.isVisible()){
29107             this.constrainXY();
29108             this.adjustAssets();
29109         }
29110         this.fireEvent("resize", this, width, height);
29111         return this;
29112     },
29113
29114
29115     /**
29116      * Resizes the dialog to fit the specified content size.
29117      * @param {Number} width
29118      * @param {Number} height
29119      * @return {Roo.BasicDialog} this
29120      */
29121     setContentSize : function(w, h){
29122         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
29123         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
29124         //if(!this.el.isBorderBox()){
29125             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
29126             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
29127         //}
29128         if(this.tabs){
29129             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
29130             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
29131         }
29132         this.resizeTo(w, h);
29133         return this;
29134     },
29135
29136     /**
29137      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
29138      * executed in response to a particular key being pressed while the dialog is active.
29139      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
29140      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
29141      * @param {Function} fn The function to call
29142      * @param {Object} scope (optional) The scope of the function
29143      * @return {Roo.BasicDialog} this
29144      */
29145     addKeyListener : function(key, fn, scope){
29146         var keyCode, shift, ctrl, alt;
29147         if(typeof key == "object" && !(key instanceof Array)){
29148             keyCode = key["key"];
29149             shift = key["shift"];
29150             ctrl = key["ctrl"];
29151             alt = key["alt"];
29152         }else{
29153             keyCode = key;
29154         }
29155         var handler = function(dlg, e){
29156             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
29157                 var k = e.getKey();
29158                 if(keyCode instanceof Array){
29159                     for(var i = 0, len = keyCode.length; i < len; i++){
29160                         if(keyCode[i] == k){
29161                           fn.call(scope || window, dlg, k, e);
29162                           return;
29163                         }
29164                     }
29165                 }else{
29166                     if(k == keyCode){
29167                         fn.call(scope || window, dlg, k, e);
29168                     }
29169                 }
29170             }
29171         };
29172         this.on("keydown", handler);
29173         return this;
29174     },
29175
29176     /**
29177      * Returns the TabPanel component (creates it if it doesn't exist).
29178      * Note: If you wish to simply check for the existence of tabs without creating them,
29179      * check for a null 'tabs' property.
29180      * @return {Roo.TabPanel} The tabs component
29181      */
29182     getTabs : function(){
29183         if(!this.tabs){
29184             this.el.addClass("x-dlg-auto-tabs");
29185             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
29186             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
29187         }
29188         return this.tabs;
29189     },
29190
29191     /**
29192      * Adds a button to the footer section of the dialog.
29193      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
29194      * object or a valid Roo.DomHelper element config
29195      * @param {Function} handler The function called when the button is clicked
29196      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
29197      * @return {Roo.Button} The new button
29198      */
29199     addButton : function(config, handler, scope){
29200         var dh = Roo.DomHelper;
29201         if(!this.footer){
29202             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
29203         }
29204         if(!this.btnContainer){
29205             var tb = this.footer.createChild({
29206
29207                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
29208                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
29209             }, null, true);
29210             this.btnContainer = tb.firstChild.firstChild.firstChild;
29211         }
29212         var bconfig = {
29213             handler: handler,
29214             scope: scope,
29215             minWidth: this.minButtonWidth,
29216             hideParent:true
29217         };
29218         if(typeof config == "string"){
29219             bconfig.text = config;
29220         }else{
29221             if(config.tag){
29222                 bconfig.dhconfig = config;
29223             }else{
29224                 Roo.apply(bconfig, config);
29225             }
29226         }
29227         var fc = false;
29228         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
29229             bconfig.position = Math.max(0, bconfig.position);
29230             fc = this.btnContainer.childNodes[bconfig.position];
29231         }
29232          
29233         var btn = new Roo.Button(
29234             fc ? 
29235                 this.btnContainer.insertBefore(document.createElement("td"),fc)
29236                 : this.btnContainer.appendChild(document.createElement("td")),
29237             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
29238             bconfig
29239         );
29240         this.syncBodyHeight();
29241         if(!this.buttons){
29242             /**
29243              * Array of all the buttons that have been added to this dialog via addButton
29244              * @type Array
29245              */
29246             this.buttons = [];
29247         }
29248         this.buttons.push(btn);
29249         return btn;
29250     },
29251
29252     /**
29253      * Sets the default button to be focused when the dialog is displayed.
29254      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
29255      * @return {Roo.BasicDialog} this
29256      */
29257     setDefaultButton : function(btn){
29258         this.defaultButton = btn;
29259         return this;
29260     },
29261
29262     // private
29263     getHeaderFooterHeight : function(safe){
29264         var height = 0;
29265         if(this.header){
29266            height += this.header.getHeight();
29267         }
29268         if(this.footer){
29269            var fm = this.footer.getMargins();
29270             height += (this.footer.getHeight()+fm.top+fm.bottom);
29271         }
29272         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
29273         height += this.centerBg.getPadding("tb");
29274         return height;
29275     },
29276
29277     // private
29278     syncBodyHeight : function(){
29279         var bd = this.body, cb = this.centerBg, bw = this.bwrap;
29280         var height = this.size.height - this.getHeaderFooterHeight(false);
29281         bd.setHeight(height-bd.getMargins("tb"));
29282         var hh = this.header.getHeight();
29283         var h = this.size.height-hh;
29284         cb.setHeight(h);
29285         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
29286         bw.setHeight(h-cb.getPadding("tb"));
29287         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
29288         bd.setWidth(bw.getWidth(true));
29289         if(this.tabs){
29290             this.tabs.syncHeight();
29291             if(Roo.isIE){
29292                 this.tabs.el.repaint();
29293             }
29294         }
29295     },
29296
29297     /**
29298      * Restores the previous state of the dialog if Roo.state is configured.
29299      * @return {Roo.BasicDialog} this
29300      */
29301     restoreState : function(){
29302         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
29303         if(box && box.width){
29304             this.xy = [box.x, box.y];
29305             this.resizeTo(box.width, box.height);
29306         }
29307         return this;
29308     },
29309
29310     // private
29311     beforeShow : function(){
29312         this.expand();
29313         if(this.fixedcenter){
29314             this.xy = this.el.getCenterXY(true);
29315         }
29316         if(this.modal){
29317             Roo.get(document.body).addClass("x-body-masked");
29318             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29319             this.mask.show();
29320         }
29321         this.constrainXY();
29322     },
29323
29324     // private
29325     animShow : function(){
29326         var b = Roo.get(this.animateTarget).getBox();
29327         this.proxy.setSize(b.width, b.height);
29328         this.proxy.setLocation(b.x, b.y);
29329         this.proxy.show();
29330         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
29331                     true, .35, this.showEl.createDelegate(this));
29332     },
29333
29334     /**
29335      * Shows the dialog.
29336      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
29337      * @return {Roo.BasicDialog} this
29338      */
29339     show : function(animateTarget){
29340         if (this.fireEvent("beforeshow", this) === false){
29341             return;
29342         }
29343         if(this.syncHeightBeforeShow){
29344             this.syncBodyHeight();
29345         }else if(this.firstShow){
29346             this.firstShow = false;
29347             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
29348         }
29349         this.animateTarget = animateTarget || this.animateTarget;
29350         if(!this.el.isVisible()){
29351             this.beforeShow();
29352             if(this.animateTarget && Roo.get(this.animateTarget)){
29353                 this.animShow();
29354             }else{
29355                 this.showEl();
29356             }
29357         }
29358         return this;
29359     },
29360
29361     // private
29362     showEl : function(){
29363         this.proxy.hide();
29364         this.el.setXY(this.xy);
29365         this.el.show();
29366         this.adjustAssets(true);
29367         this.toFront();
29368         this.focus();
29369         // IE peekaboo bug - fix found by Dave Fenwick
29370         if(Roo.isIE){
29371             this.el.repaint();
29372         }
29373         this.fireEvent("show", this);
29374     },
29375
29376     /**
29377      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
29378      * dialog itself will receive focus.
29379      */
29380     focus : function(){
29381         if(this.defaultButton){
29382             this.defaultButton.focus();
29383         }else{
29384             this.focusEl.focus();
29385         }
29386     },
29387
29388     // private
29389     constrainXY : function(){
29390         if(this.constraintoviewport !== false){
29391             if(!this.viewSize){
29392                 if(this.container){
29393                     var s = this.container.getSize();
29394                     this.viewSize = [s.width, s.height];
29395                 }else{
29396                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
29397                 }
29398             }
29399             var s = Roo.get(this.container||document).getScroll();
29400
29401             var x = this.xy[0], y = this.xy[1];
29402             var w = this.size.width, h = this.size.height;
29403             var vw = this.viewSize[0], vh = this.viewSize[1];
29404             // only move it if it needs it
29405             var moved = false;
29406             // first validate right/bottom
29407             if(x + w > vw+s.left){
29408                 x = vw - w;
29409                 moved = true;
29410             }
29411             if(y + h > vh+s.top){
29412                 y = vh - h;
29413                 moved = true;
29414             }
29415             // then make sure top/left isn't negative
29416             if(x < s.left){
29417                 x = s.left;
29418                 moved = true;
29419             }
29420             if(y < s.top){
29421                 y = s.top;
29422                 moved = true;
29423             }
29424             if(moved){
29425                 // cache xy
29426                 this.xy = [x, y];
29427                 if(this.isVisible()){
29428                     this.el.setLocation(x, y);
29429                     this.adjustAssets();
29430                 }
29431             }
29432         }
29433     },
29434
29435     // private
29436     onDrag : function(){
29437         if(!this.proxyDrag){
29438             this.xy = this.el.getXY();
29439             this.adjustAssets();
29440         }
29441     },
29442
29443     // private
29444     adjustAssets : function(doShow){
29445         var x = this.xy[0], y = this.xy[1];
29446         var w = this.size.width, h = this.size.height;
29447         if(doShow === true){
29448             if(this.shadow){
29449                 this.shadow.show(this.el);
29450             }
29451             if(this.shim){
29452                 this.shim.show();
29453             }
29454         }
29455         if(this.shadow && this.shadow.isVisible()){
29456             this.shadow.show(this.el);
29457         }
29458         if(this.shim && this.shim.isVisible()){
29459             this.shim.setBounds(x, y, w, h);
29460         }
29461     },
29462
29463     // private
29464     adjustViewport : function(w, h){
29465         if(!w || !h){
29466             w = Roo.lib.Dom.getViewWidth();
29467             h = Roo.lib.Dom.getViewHeight();
29468         }
29469         // cache the size
29470         this.viewSize = [w, h];
29471         if(this.modal && this.mask.isVisible()){
29472             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
29473             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29474         }
29475         if(this.isVisible()){
29476             this.constrainXY();
29477         }
29478     },
29479
29480     /**
29481      * Destroys this dialog and all its supporting elements (including any tabs, shim,
29482      * shadow, proxy, mask, etc.)  Also removes all event listeners.
29483      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
29484      */
29485     destroy : function(removeEl){
29486         if(this.isVisible()){
29487             this.animateTarget = null;
29488             this.hide();
29489         }
29490         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
29491         if(this.tabs){
29492             this.tabs.destroy(removeEl);
29493         }
29494         Roo.destroy(
29495              this.shim,
29496              this.proxy,
29497              this.resizer,
29498              this.close,
29499              this.mask
29500         );
29501         if(this.dd){
29502             this.dd.unreg();
29503         }
29504         if(this.buttons){
29505            for(var i = 0, len = this.buttons.length; i < len; i++){
29506                this.buttons[i].destroy();
29507            }
29508         }
29509         this.el.removeAllListeners();
29510         if(removeEl === true){
29511             this.el.update("");
29512             this.el.remove();
29513         }
29514         Roo.DialogManager.unregister(this);
29515     },
29516
29517     // private
29518     startMove : function(){
29519         if(this.proxyDrag){
29520             this.proxy.show();
29521         }
29522         if(this.constraintoviewport !== false){
29523             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
29524         }
29525     },
29526
29527     // private
29528     endMove : function(){
29529         if(!this.proxyDrag){
29530             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
29531         }else{
29532             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
29533             this.proxy.hide();
29534         }
29535         this.refreshSize();
29536         this.adjustAssets();
29537         this.focus();
29538         this.fireEvent("move", this, this.xy[0], this.xy[1]);
29539     },
29540
29541     /**
29542      * Brings this dialog to the front of any other visible dialogs
29543      * @return {Roo.BasicDialog} this
29544      */
29545     toFront : function(){
29546         Roo.DialogManager.bringToFront(this);
29547         return this;
29548     },
29549
29550     /**
29551      * Sends this dialog to the back (under) of any other visible dialogs
29552      * @return {Roo.BasicDialog} this
29553      */
29554     toBack : function(){
29555         Roo.DialogManager.sendToBack(this);
29556         return this;
29557     },
29558
29559     /**
29560      * Centers this dialog in the viewport
29561      * @return {Roo.BasicDialog} this
29562      */
29563     center : function(){
29564         var xy = this.el.getCenterXY(true);
29565         this.moveTo(xy[0], xy[1]);
29566         return this;
29567     },
29568
29569     /**
29570      * Moves the dialog's top-left corner to the specified point
29571      * @param {Number} x
29572      * @param {Number} y
29573      * @return {Roo.BasicDialog} this
29574      */
29575     moveTo : function(x, y){
29576         this.xy = [x,y];
29577         if(this.isVisible()){
29578             this.el.setXY(this.xy);
29579             this.adjustAssets();
29580         }
29581         return this;
29582     },
29583
29584     /**
29585      * Aligns the dialog to the specified element
29586      * @param {String/HTMLElement/Roo.Element} element The element to align to.
29587      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
29588      * @param {Array} offsets (optional) Offset the positioning by [x, y]
29589      * @return {Roo.BasicDialog} this
29590      */
29591     alignTo : function(element, position, offsets){
29592         this.xy = this.el.getAlignToXY(element, position, offsets);
29593         if(this.isVisible()){
29594             this.el.setXY(this.xy);
29595             this.adjustAssets();
29596         }
29597         return this;
29598     },
29599
29600     /**
29601      * Anchors an element to another element and realigns it when the window is resized.
29602      * @param {String/HTMLElement/Roo.Element} element The element to align to.
29603      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
29604      * @param {Array} offsets (optional) Offset the positioning by [x, y]
29605      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
29606      * is a number, it is used as the buffer delay (defaults to 50ms).
29607      * @return {Roo.BasicDialog} this
29608      */
29609     anchorTo : function(el, alignment, offsets, monitorScroll){
29610         var action = function(){
29611             this.alignTo(el, alignment, offsets);
29612         };
29613         Roo.EventManager.onWindowResize(action, this);
29614         var tm = typeof monitorScroll;
29615         if(tm != 'undefined'){
29616             Roo.EventManager.on(window, 'scroll', action, this,
29617                 {buffer: tm == 'number' ? monitorScroll : 50});
29618         }
29619         action.call(this);
29620         return this;
29621     },
29622
29623     /**
29624      * Returns true if the dialog is visible
29625      * @return {Boolean}
29626      */
29627     isVisible : function(){
29628         return this.el.isVisible();
29629     },
29630
29631     // private
29632     animHide : function(callback){
29633         var b = Roo.get(this.animateTarget).getBox();
29634         this.proxy.show();
29635         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
29636         this.el.hide();
29637         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
29638                     this.hideEl.createDelegate(this, [callback]));
29639     },
29640
29641     /**
29642      * Hides the dialog.
29643      * @param {Function} callback (optional) Function to call when the dialog is hidden
29644      * @return {Roo.BasicDialog} this
29645      */
29646     hide : function(callback){
29647         if (this.fireEvent("beforehide", this) === false){
29648             return;
29649         }
29650         if(this.shadow){
29651             this.shadow.hide();
29652         }
29653         if(this.shim) {
29654           this.shim.hide();
29655         }
29656         // sometimes animateTarget seems to get set.. causing problems...
29657         // this just double checks..
29658         if(this.animateTarget && Roo.get(this.animateTarget)) {
29659            this.animHide(callback);
29660         }else{
29661             this.el.hide();
29662             this.hideEl(callback);
29663         }
29664         return this;
29665     },
29666
29667     // private
29668     hideEl : function(callback){
29669         this.proxy.hide();
29670         if(this.modal){
29671             this.mask.hide();
29672             Roo.get(document.body).removeClass("x-body-masked");
29673         }
29674         this.fireEvent("hide", this);
29675         if(typeof callback == "function"){
29676             callback();
29677         }
29678     },
29679
29680     // private
29681     hideAction : function(){
29682         this.setLeft("-10000px");
29683         this.setTop("-10000px");
29684         this.setStyle("visibility", "hidden");
29685     },
29686
29687     // private
29688     refreshSize : function(){
29689         this.size = this.el.getSize();
29690         this.xy = this.el.getXY();
29691         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
29692     },
29693
29694     // private
29695     // z-index is managed by the DialogManager and may be overwritten at any time
29696     setZIndex : function(index){
29697         if(this.modal){
29698             this.mask.setStyle("z-index", index);
29699         }
29700         if(this.shim){
29701             this.shim.setStyle("z-index", ++index);
29702         }
29703         if(this.shadow){
29704             this.shadow.setZIndex(++index);
29705         }
29706         this.el.setStyle("z-index", ++index);
29707         if(this.proxy){
29708             this.proxy.setStyle("z-index", ++index);
29709         }
29710         if(this.resizer){
29711             this.resizer.proxy.setStyle("z-index", ++index);
29712         }
29713
29714         this.lastZIndex = index;
29715     },
29716
29717     /**
29718      * Returns the element for this dialog
29719      * @return {Roo.Element} The underlying dialog Element
29720      */
29721     getEl : function(){
29722         return this.el;
29723     }
29724 });
29725
29726 /**
29727  * @class Roo.DialogManager
29728  * Provides global access to BasicDialogs that have been created and
29729  * support for z-indexing (layering) multiple open dialogs.
29730  */
29731 Roo.DialogManager = function(){
29732     var list = {};
29733     var accessList = [];
29734     var front = null;
29735
29736     // private
29737     var sortDialogs = function(d1, d2){
29738         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
29739     };
29740
29741     // private
29742     var orderDialogs = function(){
29743         accessList.sort(sortDialogs);
29744         var seed = Roo.DialogManager.zseed;
29745         for(var i = 0, len = accessList.length; i < len; i++){
29746             var dlg = accessList[i];
29747             if(dlg){
29748                 dlg.setZIndex(seed + (i*10));
29749             }
29750         }
29751     };
29752
29753     return {
29754         /**
29755          * The starting z-index for BasicDialogs (defaults to 9000)
29756          * @type Number The z-index value
29757          */
29758         zseed : 9000,
29759
29760         // private
29761         register : function(dlg){
29762             list[dlg.id] = dlg;
29763             accessList.push(dlg);
29764         },
29765
29766         // private
29767         unregister : function(dlg){
29768             delete list[dlg.id];
29769             var i=0;
29770             var len=0;
29771             if(!accessList.indexOf){
29772                 for(  i = 0, len = accessList.length; i < len; i++){
29773                     if(accessList[i] == dlg){
29774                         accessList.splice(i, 1);
29775                         return;
29776                     }
29777                 }
29778             }else{
29779                  i = accessList.indexOf(dlg);
29780                 if(i != -1){
29781                     accessList.splice(i, 1);
29782                 }
29783             }
29784         },
29785
29786         /**
29787          * Gets a registered dialog by id
29788          * @param {String/Object} id The id of the dialog or a dialog
29789          * @return {Roo.BasicDialog} this
29790          */
29791         get : function(id){
29792             return typeof id == "object" ? id : list[id];
29793         },
29794
29795         /**
29796          * Brings the specified dialog to the front
29797          * @param {String/Object} dlg The id of the dialog or a dialog
29798          * @return {Roo.BasicDialog} this
29799          */
29800         bringToFront : function(dlg){
29801             dlg = this.get(dlg);
29802             if(dlg != front){
29803                 front = dlg;
29804                 dlg._lastAccess = new Date().getTime();
29805                 orderDialogs();
29806             }
29807             return dlg;
29808         },
29809
29810         /**
29811          * Sends the specified dialog to the back
29812          * @param {String/Object} dlg The id of the dialog or a dialog
29813          * @return {Roo.BasicDialog} this
29814          */
29815         sendToBack : function(dlg){
29816             dlg = this.get(dlg);
29817             dlg._lastAccess = -(new Date().getTime());
29818             orderDialogs();
29819             return dlg;
29820         },
29821
29822         /**
29823          * Hides all dialogs
29824          */
29825         hideAll : function(){
29826             for(var id in list){
29827                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
29828                     list[id].hide();
29829                 }
29830             }
29831         }
29832     };
29833 }();
29834
29835 /**
29836  * @class Roo.LayoutDialog
29837  * @extends Roo.BasicDialog
29838  * Dialog which provides adjustments for working with a layout in a Dialog.
29839  * Add your necessary layout config options to the dialog's config.<br>
29840  * Example usage (including a nested layout):
29841  * <pre><code>
29842 if(!dialog){
29843     dialog = new Roo.LayoutDialog("download-dlg", {
29844         modal: true,
29845         width:600,
29846         height:450,
29847         shadow:true,
29848         minWidth:500,
29849         minHeight:350,
29850         autoTabs:true,
29851         proxyDrag:true,
29852         // layout config merges with the dialog config
29853         center:{
29854             tabPosition: "top",
29855             alwaysShowTabs: true
29856         }
29857     });
29858     dialog.addKeyListener(27, dialog.hide, dialog);
29859     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
29860     dialog.addButton("Build It!", this.getDownload, this);
29861
29862     // we can even add nested layouts
29863     var innerLayout = new Roo.BorderLayout("dl-inner", {
29864         east: {
29865             initialSize: 200,
29866             autoScroll:true,
29867             split:true
29868         },
29869         center: {
29870             autoScroll:true
29871         }
29872     });
29873     innerLayout.beginUpdate();
29874     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
29875     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
29876     innerLayout.endUpdate(true);
29877
29878     var layout = dialog.getLayout();
29879     layout.beginUpdate();
29880     layout.add("center", new Roo.ContentPanel("standard-panel",
29881                         {title: "Download the Source", fitToFrame:true}));
29882     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
29883                {title: "Build your own roo.js"}));
29884     layout.getRegion("center").showPanel(sp);
29885     layout.endUpdate();
29886 }
29887 </code></pre>
29888     * @constructor
29889     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
29890     * @param {Object} config configuration options
29891   */
29892 Roo.LayoutDialog = function(el, cfg){
29893     
29894     var config=  cfg;
29895     if (typeof(cfg) == 'undefined') {
29896         config = Roo.apply({}, el);
29897         // not sure why we use documentElement here.. - it should always be body.
29898         // IE7 borks horribly if we use documentElement.
29899         // webkit also does not like documentElement - it creates a body element...
29900         el = Roo.get( document.body || document.documentElement ).createChild();
29901         //config.autoCreate = true;
29902     }
29903     
29904     
29905     config.autoTabs = false;
29906     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
29907     this.body.setStyle({overflow:"hidden", position:"relative"});
29908     this.layout = new Roo.BorderLayout(this.body.dom, config);
29909     this.layout.monitorWindowResize = false;
29910     this.el.addClass("x-dlg-auto-layout");
29911     // fix case when center region overwrites center function
29912     this.center = Roo.BasicDialog.prototype.center;
29913     this.on("show", this.layout.layout, this.layout, true);
29914     if (config.items) {
29915         var xitems = config.items;
29916         delete config.items;
29917         Roo.each(xitems, this.addxtype, this);
29918     }
29919     
29920     
29921 };
29922 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
29923     /**
29924      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
29925      * @deprecated
29926      */
29927     endUpdate : function(){
29928         this.layout.endUpdate();
29929     },
29930
29931     /**
29932      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
29933      *  @deprecated
29934      */
29935     beginUpdate : function(){
29936         this.layout.beginUpdate();
29937     },
29938
29939     /**
29940      * Get the BorderLayout for this dialog
29941      * @return {Roo.BorderLayout}
29942      */
29943     getLayout : function(){
29944         return this.layout;
29945     },
29946
29947     showEl : function(){
29948         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
29949         if(Roo.isIE7){
29950             this.layout.layout();
29951         }
29952     },
29953
29954     // private
29955     // Use the syncHeightBeforeShow config option to control this automatically
29956     syncBodyHeight : function(){
29957         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
29958         if(this.layout){this.layout.layout();}
29959     },
29960     
29961       /**
29962      * Add an xtype element (actually adds to the layout.)
29963      * @return {Object} xdata xtype object data.
29964      */
29965     
29966     addxtype : function(c) {
29967         return this.layout.addxtype(c);
29968     }
29969 });/*
29970  * Based on:
29971  * Ext JS Library 1.1.1
29972  * Copyright(c) 2006-2007, Ext JS, LLC.
29973  *
29974  * Originally Released Under LGPL - original licence link has changed is not relivant.
29975  *
29976  * Fork - LGPL
29977  * <script type="text/javascript">
29978  */
29979  
29980 /**
29981  * @class Roo.MessageBox
29982  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
29983  * Example usage:
29984  *<pre><code>
29985 // Basic alert:
29986 Roo.Msg.alert('Status', 'Changes saved successfully.');
29987
29988 // Prompt for user data:
29989 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
29990     if (btn == 'ok'){
29991         // process text value...
29992     }
29993 });
29994
29995 // Show a dialog using config options:
29996 Roo.Msg.show({
29997    title:'Save Changes?',
29998    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
29999    buttons: Roo.Msg.YESNOCANCEL,
30000    fn: processResult,
30001    animEl: 'elId'
30002 });
30003 </code></pre>
30004  * @singleton
30005  */
30006 Roo.MessageBox = function(){
30007     var dlg, opt, mask, waitTimer;
30008     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
30009     var buttons, activeTextEl, bwidth;
30010
30011     // private
30012     var handleButton = function(button){
30013         dlg.hide();
30014         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
30015     };
30016
30017     // private
30018     var handleHide = function(){
30019         if(opt && opt.cls){
30020             dlg.el.removeClass(opt.cls);
30021         }
30022         if(waitTimer){
30023             Roo.TaskMgr.stop(waitTimer);
30024             waitTimer = null;
30025         }
30026     };
30027
30028     // private
30029     var updateButtons = function(b){
30030         var width = 0;
30031         if(!b){
30032             buttons["ok"].hide();
30033             buttons["cancel"].hide();
30034             buttons["yes"].hide();
30035             buttons["no"].hide();
30036             dlg.footer.dom.style.display = 'none';
30037             return width;
30038         }
30039         dlg.footer.dom.style.display = '';
30040         for(var k in buttons){
30041             if(typeof buttons[k] != "function"){
30042                 if(b[k]){
30043                     buttons[k].show();
30044                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
30045                     width += buttons[k].el.getWidth()+15;
30046                 }else{
30047                     buttons[k].hide();
30048                 }
30049             }
30050         }
30051         return width;
30052     };
30053
30054     // private
30055     var handleEsc = function(d, k, e){
30056         if(opt && opt.closable !== false){
30057             dlg.hide();
30058         }
30059         if(e){
30060             e.stopEvent();
30061         }
30062     };
30063
30064     return {
30065         /**
30066          * Returns a reference to the underlying {@link Roo.BasicDialog} element
30067          * @return {Roo.BasicDialog} The BasicDialog element
30068          */
30069         getDialog : function(){
30070            if(!dlg){
30071                 dlg = new Roo.BasicDialog("x-msg-box", {
30072                     autoCreate : true,
30073                     shadow: true,
30074                     draggable: true,
30075                     resizable:false,
30076                     constraintoviewport:false,
30077                     fixedcenter:true,
30078                     collapsible : false,
30079                     shim:true,
30080                     modal: true,
30081                     width:400, height:100,
30082                     buttonAlign:"center",
30083                     closeClick : function(){
30084                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
30085                             handleButton("no");
30086                         }else{
30087                             handleButton("cancel");
30088                         }
30089                     }
30090                 });
30091                 dlg.on("hide", handleHide);
30092                 mask = dlg.mask;
30093                 dlg.addKeyListener(27, handleEsc);
30094                 buttons = {};
30095                 var bt = this.buttonText;
30096                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
30097                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
30098                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
30099                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
30100                 bodyEl = dlg.body.createChild({
30101
30102                     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>'
30103                 });
30104                 msgEl = bodyEl.dom.firstChild;
30105                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
30106                 textboxEl.enableDisplayMode();
30107                 textboxEl.addKeyListener([10,13], function(){
30108                     if(dlg.isVisible() && opt && opt.buttons){
30109                         if(opt.buttons.ok){
30110                             handleButton("ok");
30111                         }else if(opt.buttons.yes){
30112                             handleButton("yes");
30113                         }
30114                     }
30115                 });
30116                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
30117                 textareaEl.enableDisplayMode();
30118                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
30119                 progressEl.enableDisplayMode();
30120                 var pf = progressEl.dom.firstChild;
30121                 if (pf) {
30122                     pp = Roo.get(pf.firstChild);
30123                     pp.setHeight(pf.offsetHeight);
30124                 }
30125                 
30126             }
30127             return dlg;
30128         },
30129
30130         /**
30131          * Updates the message box body text
30132          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
30133          * the XHTML-compliant non-breaking space character '&amp;#160;')
30134          * @return {Roo.MessageBox} This message box
30135          */
30136         updateText : function(text){
30137             if(!dlg.isVisible() && !opt.width){
30138                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
30139             }
30140             msgEl.innerHTML = text || '&#160;';
30141             var w = Math.max(Math.min(opt.width || msgEl.offsetWidth, this.maxWidth), 
30142                         Math.max(opt.minWidth || this.minWidth, bwidth));
30143             if(opt.prompt){
30144                 activeTextEl.setWidth(w);
30145             }
30146             if(dlg.isVisible()){
30147                 dlg.fixedcenter = false;
30148             }
30149             // to big, make it scoll.
30150             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
30151                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
30152                 bodyEl.dom.style.overflowY = 'auto';
30153             } else {
30154                 bodyEl.dom.style.height = '';
30155                 bodyEl.dom.style.overflowY = '';
30156             }
30157             
30158             dlg.setContentSize(w, bodyEl.getHeight());
30159             if(dlg.isVisible()){
30160                 dlg.fixedcenter = true;
30161             }
30162             return this;
30163         },
30164
30165         /**
30166          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
30167          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
30168          * @param {Number} value Any number between 0 and 1 (e.g., .5)
30169          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
30170          * @return {Roo.MessageBox} This message box
30171          */
30172         updateProgress : function(value, text){
30173             if(text){
30174                 this.updateText(text);
30175             }
30176             if (pp) { // weird bug on my firefox - for some reason this is not defined
30177                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
30178             }
30179             return this;
30180         },        
30181
30182         /**
30183          * Returns true if the message box is currently displayed
30184          * @return {Boolean} True if the message box is visible, else false
30185          */
30186         isVisible : function(){
30187             return dlg && dlg.isVisible();  
30188         },
30189
30190         /**
30191          * Hides the message box if it is displayed
30192          */
30193         hide : function(){
30194             if(this.isVisible()){
30195                 dlg.hide();
30196             }  
30197         },
30198
30199         /**
30200          * Displays a new message box, or reinitializes an existing message box, based on the config options
30201          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
30202          * The following config object properties are supported:
30203          * <pre>
30204 Property    Type             Description
30205 ----------  ---------------  ------------------------------------------------------------------------------------
30206 animEl            String/Element   An id or Element from which the message box should animate as it opens and
30207                                    closes (defaults to undefined)
30208 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
30209                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
30210 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
30211                                    progress and wait dialogs will ignore this property and always hide the
30212                                    close button as they can only be closed programmatically.
30213 cls               String           A custom CSS class to apply to the message box element
30214 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
30215                                    displayed (defaults to 75)
30216 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
30217                                    function will be btn (the name of the button that was clicked, if applicable,
30218                                    e.g. "ok"), and text (the value of the active text field, if applicable).
30219                                    Progress and wait dialogs will ignore this option since they do not respond to
30220                                    user actions and can only be closed programmatically, so any required function
30221                                    should be called by the same code after it closes the dialog.
30222 icon              String           A CSS class that provides a background image to be used as an icon for
30223                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
30224 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
30225 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
30226 modal             Boolean          False to allow user interaction with the page while the message box is
30227                                    displayed (defaults to true)
30228 msg               String           A string that will replace the existing message box body text (defaults
30229                                    to the XHTML-compliant non-breaking space character '&#160;')
30230 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
30231 progress          Boolean          True to display a progress bar (defaults to false)
30232 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
30233 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
30234 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
30235 title             String           The title text
30236 value             String           The string value to set into the active textbox element if displayed
30237 wait              Boolean          True to display a progress bar (defaults to false)
30238 width             Number           The width of the dialog in pixels
30239 </pre>
30240          *
30241          * Example usage:
30242          * <pre><code>
30243 Roo.Msg.show({
30244    title: 'Address',
30245    msg: 'Please enter your address:',
30246    width: 300,
30247    buttons: Roo.MessageBox.OKCANCEL,
30248    multiline: true,
30249    fn: saveAddress,
30250    animEl: 'addAddressBtn'
30251 });
30252 </code></pre>
30253          * @param {Object} config Configuration options
30254          * @return {Roo.MessageBox} This message box
30255          */
30256         show : function(options)
30257         {
30258             
30259             // this causes nightmares if you show one dialog after another
30260             // especially on callbacks..
30261              
30262             if(this.isVisible()){
30263                 
30264                 this.hide();
30265                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML )
30266                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
30267                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
30268                 
30269             }
30270             var d = this.getDialog();
30271             opt = options;
30272             d.setTitle(opt.title || "&#160;");
30273             d.close.setDisplayed(opt.closable !== false);
30274             activeTextEl = textboxEl;
30275             opt.prompt = opt.prompt || (opt.multiline ? true : false);
30276             if(opt.prompt){
30277                 if(opt.multiline){
30278                     textboxEl.hide();
30279                     textareaEl.show();
30280                     textareaEl.setHeight(typeof opt.multiline == "number" ?
30281                         opt.multiline : this.defaultTextHeight);
30282                     activeTextEl = textareaEl;
30283                 }else{
30284                     textboxEl.show();
30285                     textareaEl.hide();
30286                 }
30287             }else{
30288                 textboxEl.hide();
30289                 textareaEl.hide();
30290             }
30291             progressEl.setDisplayed(opt.progress === true);
30292             this.updateProgress(0);
30293             activeTextEl.dom.value = opt.value || "";
30294             if(opt.prompt){
30295                 dlg.setDefaultButton(activeTextEl);
30296             }else{
30297                 var bs = opt.buttons;
30298                 var db = null;
30299                 if(bs && bs.ok){
30300                     db = buttons["ok"];
30301                 }else if(bs && bs.yes){
30302                     db = buttons["yes"];
30303                 }
30304                 dlg.setDefaultButton(db);
30305             }
30306             bwidth = updateButtons(opt.buttons);
30307             this.updateText(opt.msg);
30308             if(opt.cls){
30309                 d.el.addClass(opt.cls);
30310             }
30311             d.proxyDrag = opt.proxyDrag === true;
30312             d.modal = opt.modal !== false;
30313             d.mask = opt.modal !== false ? mask : false;
30314             if(!d.isVisible()){
30315                 // force it to the end of the z-index stack so it gets a cursor in FF
30316                 document.body.appendChild(dlg.el.dom);
30317                 d.animateTarget = null;
30318                 d.show(options.animEl);
30319             }
30320             return this;
30321         },
30322
30323         /**
30324          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
30325          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
30326          * and closing the message box when the process is complete.
30327          * @param {String} title The title bar text
30328          * @param {String} msg The message box body text
30329          * @return {Roo.MessageBox} This message box
30330          */
30331         progress : function(title, msg){
30332             this.show({
30333                 title : title,
30334                 msg : msg,
30335                 buttons: false,
30336                 progress:true,
30337                 closable:false,
30338                 minWidth: this.minProgressWidth,
30339                 modal : true
30340             });
30341             return this;
30342         },
30343
30344         /**
30345          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
30346          * If a callback function is passed it will be called after the user clicks the button, and the
30347          * id of the button that was clicked will be passed as the only parameter to the callback
30348          * (could also be the top-right close button).
30349          * @param {String} title The title bar text
30350          * @param {String} msg The message box body text
30351          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30352          * @param {Object} scope (optional) The scope of the callback function
30353          * @return {Roo.MessageBox} This message box
30354          */
30355         alert : function(title, msg, fn, scope){
30356             this.show({
30357                 title : title,
30358                 msg : msg,
30359                 buttons: this.OK,
30360                 fn: fn,
30361                 scope : scope,
30362                 modal : true
30363             });
30364             return this;
30365         },
30366
30367         /**
30368          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
30369          * interaction while waiting for a long-running process to complete that does not have defined intervals.
30370          * You are responsible for closing the message box when the process is complete.
30371          * @param {String} msg The message box body text
30372          * @param {String} title (optional) The title bar text
30373          * @return {Roo.MessageBox} This message box
30374          */
30375         wait : function(msg, title){
30376             this.show({
30377                 title : title,
30378                 msg : msg,
30379                 buttons: false,
30380                 closable:false,
30381                 progress:true,
30382                 modal:true,
30383                 width:300,
30384                 wait:true
30385             });
30386             waitTimer = Roo.TaskMgr.start({
30387                 run: function(i){
30388                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
30389                 },
30390                 interval: 1000
30391             });
30392             return this;
30393         },
30394
30395         /**
30396          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
30397          * If a callback function is passed it will be called after the user clicks either button, and the id of the
30398          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
30399          * @param {String} title The title bar text
30400          * @param {String} msg The message box body text
30401          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30402          * @param {Object} scope (optional) The scope of the callback function
30403          * @return {Roo.MessageBox} This message box
30404          */
30405         confirm : function(title, msg, fn, scope){
30406             this.show({
30407                 title : title,
30408                 msg : msg,
30409                 buttons: this.YESNO,
30410                 fn: fn,
30411                 scope : scope,
30412                 modal : true
30413             });
30414             return this;
30415         },
30416
30417         /**
30418          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
30419          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
30420          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
30421          * (could also be the top-right close button) and the text that was entered will be passed as the two
30422          * parameters to the callback.
30423          * @param {String} title The title bar text
30424          * @param {String} msg The message box body text
30425          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30426          * @param {Object} scope (optional) The scope of the callback function
30427          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
30428          * property, or the height in pixels to create the textbox (defaults to false / single-line)
30429          * @return {Roo.MessageBox} This message box
30430          */
30431         prompt : function(title, msg, fn, scope, multiline){
30432             this.show({
30433                 title : title,
30434                 msg : msg,
30435                 buttons: this.OKCANCEL,
30436                 fn: fn,
30437                 minWidth:250,
30438                 scope : scope,
30439                 prompt:true,
30440                 multiline: multiline,
30441                 modal : true
30442             });
30443             return this;
30444         },
30445
30446         /**
30447          * Button config that displays a single OK button
30448          * @type Object
30449          */
30450         OK : {ok:true},
30451         /**
30452          * Button config that displays Yes and No buttons
30453          * @type Object
30454          */
30455         YESNO : {yes:true, no:true},
30456         /**
30457          * Button config that displays OK and Cancel buttons
30458          * @type Object
30459          */
30460         OKCANCEL : {ok:true, cancel:true},
30461         /**
30462          * Button config that displays Yes, No and Cancel buttons
30463          * @type Object
30464          */
30465         YESNOCANCEL : {yes:true, no:true, cancel:true},
30466
30467         /**
30468          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
30469          * @type Number
30470          */
30471         defaultTextHeight : 75,
30472         /**
30473          * The maximum width in pixels of the message box (defaults to 600)
30474          * @type Number
30475          */
30476         maxWidth : 600,
30477         /**
30478          * The minimum width in pixels of the message box (defaults to 100)
30479          * @type Number
30480          */
30481         minWidth : 100,
30482         /**
30483          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
30484          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
30485          * @type Number
30486          */
30487         minProgressWidth : 250,
30488         /**
30489          * An object containing the default button text strings that can be overriden for localized language support.
30490          * Supported properties are: ok, cancel, yes and no.
30491          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
30492          * @type Object
30493          */
30494         buttonText : {
30495             ok : "OK",
30496             cancel : "Cancel",
30497             yes : "Yes",
30498             no : "No"
30499         }
30500     };
30501 }();
30502
30503 /**
30504  * Shorthand for {@link Roo.MessageBox}
30505  */
30506 Roo.Msg = Roo.MessageBox;/*
30507  * Based on:
30508  * Ext JS Library 1.1.1
30509  * Copyright(c) 2006-2007, Ext JS, LLC.
30510  *
30511  * Originally Released Under LGPL - original licence link has changed is not relivant.
30512  *
30513  * Fork - LGPL
30514  * <script type="text/javascript">
30515  */
30516 /**
30517  * @class Roo.QuickTips
30518  * Provides attractive and customizable tooltips for any element.
30519  * @singleton
30520  */
30521 Roo.QuickTips = function(){
30522     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
30523     var ce, bd, xy, dd;
30524     var visible = false, disabled = true, inited = false;
30525     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
30526     
30527     var onOver = function(e){
30528         if(disabled){
30529             return;
30530         }
30531         var t = e.getTarget();
30532         if(!t || t.nodeType !== 1 || t == document || t == document.body){
30533             return;
30534         }
30535         if(ce && t == ce.el){
30536             clearTimeout(hideProc);
30537             return;
30538         }
30539         if(t && tagEls[t.id]){
30540             tagEls[t.id].el = t;
30541             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
30542             return;
30543         }
30544         var ttp, et = Roo.fly(t);
30545         var ns = cfg.namespace;
30546         if(tm.interceptTitles && t.title){
30547             ttp = t.title;
30548             t.qtip = ttp;
30549             t.removeAttribute("title");
30550             e.preventDefault();
30551         }else{
30552             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
30553         }
30554         if(ttp){
30555             showProc = show.defer(tm.showDelay, tm, [{
30556                 el: t, 
30557                 text: ttp, 
30558                 width: et.getAttributeNS(ns, cfg.width),
30559                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
30560                 title: et.getAttributeNS(ns, cfg.title),
30561                     cls: et.getAttributeNS(ns, cfg.cls)
30562             }]);
30563         }
30564     };
30565     
30566     var onOut = function(e){
30567         clearTimeout(showProc);
30568         var t = e.getTarget();
30569         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
30570             hideProc = setTimeout(hide, tm.hideDelay);
30571         }
30572     };
30573     
30574     var onMove = function(e){
30575         if(disabled){
30576             return;
30577         }
30578         xy = e.getXY();
30579         xy[1] += 18;
30580         if(tm.trackMouse && ce){
30581             el.setXY(xy);
30582         }
30583     };
30584     
30585     var onDown = function(e){
30586         clearTimeout(showProc);
30587         clearTimeout(hideProc);
30588         if(!e.within(el)){
30589             if(tm.hideOnClick){
30590                 hide();
30591                 tm.disable();
30592                 tm.enable.defer(100, tm);
30593             }
30594         }
30595     };
30596     
30597     var getPad = function(){
30598         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
30599     };
30600
30601     var show = function(o){
30602         if(disabled){
30603             return;
30604         }
30605         clearTimeout(dismissProc);
30606         ce = o;
30607         if(removeCls){ // in case manually hidden
30608             el.removeClass(removeCls);
30609             removeCls = null;
30610         }
30611         if(ce.cls){
30612             el.addClass(ce.cls);
30613             removeCls = ce.cls;
30614         }
30615         if(ce.title){
30616             tipTitle.update(ce.title);
30617             tipTitle.show();
30618         }else{
30619             tipTitle.update('');
30620             tipTitle.hide();
30621         }
30622         el.dom.style.width  = tm.maxWidth+'px';
30623         //tipBody.dom.style.width = '';
30624         tipBodyText.update(o.text);
30625         var p = getPad(), w = ce.width;
30626         if(!w){
30627             var td = tipBodyText.dom;
30628             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
30629             if(aw > tm.maxWidth){
30630                 w = tm.maxWidth;
30631             }else if(aw < tm.minWidth){
30632                 w = tm.minWidth;
30633             }else{
30634                 w = aw;
30635             }
30636         }
30637         //tipBody.setWidth(w);
30638         el.setWidth(parseInt(w, 10) + p);
30639         if(ce.autoHide === false){
30640             close.setDisplayed(true);
30641             if(dd){
30642                 dd.unlock();
30643             }
30644         }else{
30645             close.setDisplayed(false);
30646             if(dd){
30647                 dd.lock();
30648             }
30649         }
30650         if(xy){
30651             el.avoidY = xy[1]-18;
30652             el.setXY(xy);
30653         }
30654         if(tm.animate){
30655             el.setOpacity(.1);
30656             el.setStyle("visibility", "visible");
30657             el.fadeIn({callback: afterShow});
30658         }else{
30659             afterShow();
30660         }
30661     };
30662     
30663     var afterShow = function(){
30664         if(ce){
30665             el.show();
30666             esc.enable();
30667             if(tm.autoDismiss && ce.autoHide !== false){
30668                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
30669             }
30670         }
30671     };
30672     
30673     var hide = function(noanim){
30674         clearTimeout(dismissProc);
30675         clearTimeout(hideProc);
30676         ce = null;
30677         if(el.isVisible()){
30678             esc.disable();
30679             if(noanim !== true && tm.animate){
30680                 el.fadeOut({callback: afterHide});
30681             }else{
30682                 afterHide();
30683             } 
30684         }
30685     };
30686     
30687     var afterHide = function(){
30688         el.hide();
30689         if(removeCls){
30690             el.removeClass(removeCls);
30691             removeCls = null;
30692         }
30693     };
30694     
30695     return {
30696         /**
30697         * @cfg {Number} minWidth
30698         * The minimum width of the quick tip (defaults to 40)
30699         */
30700        minWidth : 40,
30701         /**
30702         * @cfg {Number} maxWidth
30703         * The maximum width of the quick tip (defaults to 300)
30704         */
30705        maxWidth : 300,
30706         /**
30707         * @cfg {Boolean} interceptTitles
30708         * True to automatically use the element's DOM title value if available (defaults to false)
30709         */
30710        interceptTitles : false,
30711         /**
30712         * @cfg {Boolean} trackMouse
30713         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
30714         */
30715        trackMouse : false,
30716         /**
30717         * @cfg {Boolean} hideOnClick
30718         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
30719         */
30720        hideOnClick : true,
30721         /**
30722         * @cfg {Number} showDelay
30723         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
30724         */
30725        showDelay : 500,
30726         /**
30727         * @cfg {Number} hideDelay
30728         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
30729         */
30730        hideDelay : 200,
30731         /**
30732         * @cfg {Boolean} autoHide
30733         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
30734         * Used in conjunction with hideDelay.
30735         */
30736        autoHide : true,
30737         /**
30738         * @cfg {Boolean}
30739         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
30740         * (defaults to true).  Used in conjunction with autoDismissDelay.
30741         */
30742        autoDismiss : true,
30743         /**
30744         * @cfg {Number}
30745         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
30746         */
30747        autoDismissDelay : 5000,
30748        /**
30749         * @cfg {Boolean} animate
30750         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
30751         */
30752        animate : false,
30753
30754        /**
30755         * @cfg {String} title
30756         * Title text to display (defaults to '').  This can be any valid HTML markup.
30757         */
30758         title: '',
30759        /**
30760         * @cfg {String} text
30761         * Body text to display (defaults to '').  This can be any valid HTML markup.
30762         */
30763         text : '',
30764        /**
30765         * @cfg {String} cls
30766         * A CSS class to apply to the base quick tip element (defaults to '').
30767         */
30768         cls : '',
30769        /**
30770         * @cfg {Number} width
30771         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
30772         * minWidth or maxWidth.
30773         */
30774         width : null,
30775
30776     /**
30777      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
30778      * or display QuickTips in a page.
30779      */
30780        init : function(){
30781           tm = Roo.QuickTips;
30782           cfg = tm.tagConfig;
30783           if(!inited){
30784               if(!Roo.isReady){ // allow calling of init() before onReady
30785                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
30786                   return;
30787               }
30788               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
30789               el.fxDefaults = {stopFx: true};
30790               // maximum custom styling
30791               //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>');
30792               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>');              
30793               tipTitle = el.child('h3');
30794               tipTitle.enableDisplayMode("block");
30795               tipBody = el.child('div.x-tip-bd');
30796               tipBodyText = el.child('div.x-tip-bd-inner');
30797               //bdLeft = el.child('div.x-tip-bd-left');
30798               //bdRight = el.child('div.x-tip-bd-right');
30799               close = el.child('div.x-tip-close');
30800               close.enableDisplayMode("block");
30801               close.on("click", hide);
30802               var d = Roo.get(document);
30803               d.on("mousedown", onDown);
30804               d.on("mouseover", onOver);
30805               d.on("mouseout", onOut);
30806               d.on("mousemove", onMove);
30807               esc = d.addKeyListener(27, hide);
30808               esc.disable();
30809               if(Roo.dd.DD){
30810                   dd = el.initDD("default", null, {
30811                       onDrag : function(){
30812                           el.sync();  
30813                       }
30814                   });
30815                   dd.setHandleElId(tipTitle.id);
30816                   dd.lock();
30817               }
30818               inited = true;
30819           }
30820           this.enable(); 
30821        },
30822
30823     /**
30824      * Configures a new quick tip instance and assigns it to a target element.  The following config options
30825      * are supported:
30826      * <pre>
30827 Property    Type                   Description
30828 ----------  ---------------------  ------------------------------------------------------------------------
30829 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
30830      * </ul>
30831      * @param {Object} config The config object
30832      */
30833        register : function(config){
30834            var cs = config instanceof Array ? config : arguments;
30835            for(var i = 0, len = cs.length; i < len; i++) {
30836                var c = cs[i];
30837                var target = c.target;
30838                if(target){
30839                    if(target instanceof Array){
30840                        for(var j = 0, jlen = target.length; j < jlen; j++){
30841                            tagEls[target[j]] = c;
30842                        }
30843                    }else{
30844                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
30845                    }
30846                }
30847            }
30848        },
30849
30850     /**
30851      * Removes this quick tip from its element and destroys it.
30852      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
30853      */
30854        unregister : function(el){
30855            delete tagEls[Roo.id(el)];
30856        },
30857
30858     /**
30859      * Enable this quick tip.
30860      */
30861        enable : function(){
30862            if(inited && disabled){
30863                locks.pop();
30864                if(locks.length < 1){
30865                    disabled = false;
30866                }
30867            }
30868        },
30869
30870     /**
30871      * Disable this quick tip.
30872      */
30873        disable : function(){
30874           disabled = true;
30875           clearTimeout(showProc);
30876           clearTimeout(hideProc);
30877           clearTimeout(dismissProc);
30878           if(ce){
30879               hide(true);
30880           }
30881           locks.push(1);
30882        },
30883
30884     /**
30885      * Returns true if the quick tip is enabled, else false.
30886      */
30887        isEnabled : function(){
30888             return !disabled;
30889        },
30890
30891         // private
30892        tagConfig : {
30893            namespace : "ext",
30894            attribute : "qtip",
30895            width : "width",
30896            target : "target",
30897            title : "qtitle",
30898            hide : "hide",
30899            cls : "qclass"
30900        }
30901    };
30902 }();
30903
30904 // backwards compat
30905 Roo.QuickTips.tips = Roo.QuickTips.register;/*
30906  * Based on:
30907  * Ext JS Library 1.1.1
30908  * Copyright(c) 2006-2007, Ext JS, LLC.
30909  *
30910  * Originally Released Under LGPL - original licence link has changed is not relivant.
30911  *
30912  * Fork - LGPL
30913  * <script type="text/javascript">
30914  */
30915  
30916
30917 /**
30918  * @class Roo.tree.TreePanel
30919  * @extends Roo.data.Tree
30920
30921  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
30922  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
30923  * @cfg {Boolean} enableDD true to enable drag and drop
30924  * @cfg {Boolean} enableDrag true to enable just drag
30925  * @cfg {Boolean} enableDrop true to enable just drop
30926  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
30927  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
30928  * @cfg {String} ddGroup The DD group this TreePanel belongs to
30929  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
30930  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
30931  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
30932  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
30933  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
30934  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
30935  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
30936  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
30937  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
30938  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
30939  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
30940  * @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>
30941  * @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>
30942  * 
30943  * @constructor
30944  * @param {String/HTMLElement/Element} el The container element
30945  * @param {Object} config
30946  */
30947 Roo.tree.TreePanel = function(el, config){
30948     var root = false;
30949     var loader = false;
30950     if (config.root) {
30951         root = config.root;
30952         delete config.root;
30953     }
30954     if (config.loader) {
30955         loader = config.loader;
30956         delete config.loader;
30957     }
30958     
30959     Roo.apply(this, config);
30960     Roo.tree.TreePanel.superclass.constructor.call(this);
30961     this.el = Roo.get(el);
30962     this.el.addClass('x-tree');
30963     //console.log(root);
30964     if (root) {
30965         this.setRootNode( Roo.factory(root, Roo.tree));
30966     }
30967     if (loader) {
30968         this.loader = Roo.factory(loader, Roo.tree);
30969     }
30970    /**
30971     * Read-only. The id of the container element becomes this TreePanel's id.
30972     */
30973     this.id = this.el.id;
30974     this.addEvents({
30975         /**
30976         * @event beforeload
30977         * Fires before a node is loaded, return false to cancel
30978         * @param {Node} node The node being loaded
30979         */
30980         "beforeload" : true,
30981         /**
30982         * @event load
30983         * Fires when a node is loaded
30984         * @param {Node} node The node that was loaded
30985         */
30986         "load" : true,
30987         /**
30988         * @event textchange
30989         * Fires when the text for a node is changed
30990         * @param {Node} node The node
30991         * @param {String} text The new text
30992         * @param {String} oldText The old text
30993         */
30994         "textchange" : true,
30995         /**
30996         * @event beforeexpand
30997         * Fires before a node is expanded, return false to cancel.
30998         * @param {Node} node The node
30999         * @param {Boolean} deep
31000         * @param {Boolean} anim
31001         */
31002         "beforeexpand" : true,
31003         /**
31004         * @event beforecollapse
31005         * Fires before a node is collapsed, return false to cancel.
31006         * @param {Node} node The node
31007         * @param {Boolean} deep
31008         * @param {Boolean} anim
31009         */
31010         "beforecollapse" : true,
31011         /**
31012         * @event expand
31013         * Fires when a node is expanded
31014         * @param {Node} node The node
31015         */
31016         "expand" : true,
31017         /**
31018         * @event disabledchange
31019         * Fires when the disabled status of a node changes
31020         * @param {Node} node The node
31021         * @param {Boolean} disabled
31022         */
31023         "disabledchange" : true,
31024         /**
31025         * @event collapse
31026         * Fires when a node is collapsed
31027         * @param {Node} node The node
31028         */
31029         "collapse" : true,
31030         /**
31031         * @event beforeclick
31032         * Fires before click processing on a node. Return false to cancel the default action.
31033         * @param {Node} node The node
31034         * @param {Roo.EventObject} e The event object
31035         */
31036         "beforeclick":true,
31037         /**
31038         * @event checkchange
31039         * Fires when a node with a checkbox's checked property changes
31040         * @param {Node} this This node
31041         * @param {Boolean} checked
31042         */
31043         "checkchange":true,
31044         /**
31045         * @event click
31046         * Fires when a node is clicked
31047         * @param {Node} node The node
31048         * @param {Roo.EventObject} e The event object
31049         */
31050         "click":true,
31051         /**
31052         * @event dblclick
31053         * Fires when a node is double clicked
31054         * @param {Node} node The node
31055         * @param {Roo.EventObject} e The event object
31056         */
31057         "dblclick":true,
31058         /**
31059         * @event contextmenu
31060         * Fires when a node is right clicked
31061         * @param {Node} node The node
31062         * @param {Roo.EventObject} e The event object
31063         */
31064         "contextmenu":true,
31065         /**
31066         * @event beforechildrenrendered
31067         * Fires right before the child nodes for a node are rendered
31068         * @param {Node} node The node
31069         */
31070         "beforechildrenrendered":true,
31071         /**
31072         * @event startdrag
31073         * Fires when a node starts being dragged
31074         * @param {Roo.tree.TreePanel} this
31075         * @param {Roo.tree.TreeNode} node
31076         * @param {event} e The raw browser event
31077         */ 
31078        "startdrag" : true,
31079        /**
31080         * @event enddrag
31081         * Fires when a drag operation is complete
31082         * @param {Roo.tree.TreePanel} this
31083         * @param {Roo.tree.TreeNode} node
31084         * @param {event} e The raw browser event
31085         */
31086        "enddrag" : true,
31087        /**
31088         * @event dragdrop
31089         * Fires when a dragged node is dropped on a valid DD target
31090         * @param {Roo.tree.TreePanel} this
31091         * @param {Roo.tree.TreeNode} node
31092         * @param {DD} dd The dd it was dropped on
31093         * @param {event} e The raw browser event
31094         */
31095        "dragdrop" : true,
31096        /**
31097         * @event beforenodedrop
31098         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
31099         * passed to handlers has the following properties:<br />
31100         * <ul style="padding:5px;padding-left:16px;">
31101         * <li>tree - The TreePanel</li>
31102         * <li>target - The node being targeted for the drop</li>
31103         * <li>data - The drag data from the drag source</li>
31104         * <li>point - The point of the drop - append, above or below</li>
31105         * <li>source - The drag source</li>
31106         * <li>rawEvent - Raw mouse event</li>
31107         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
31108         * to be inserted by setting them on this object.</li>
31109         * <li>cancel - Set this to true to cancel the drop.</li>
31110         * </ul>
31111         * @param {Object} dropEvent
31112         */
31113        "beforenodedrop" : true,
31114        /**
31115         * @event nodedrop
31116         * Fires after a DD object is dropped on a node in this tree. The dropEvent
31117         * passed to handlers has the following properties:<br />
31118         * <ul style="padding:5px;padding-left:16px;">
31119         * <li>tree - The TreePanel</li>
31120         * <li>target - The node being targeted for the drop</li>
31121         * <li>data - The drag data from the drag source</li>
31122         * <li>point - The point of the drop - append, above or below</li>
31123         * <li>source - The drag source</li>
31124         * <li>rawEvent - Raw mouse event</li>
31125         * <li>dropNode - Dropped node(s).</li>
31126         * </ul>
31127         * @param {Object} dropEvent
31128         */
31129        "nodedrop" : true,
31130         /**
31131         * @event nodedragover
31132         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
31133         * passed to handlers has the following properties:<br />
31134         * <ul style="padding:5px;padding-left:16px;">
31135         * <li>tree - The TreePanel</li>
31136         * <li>target - The node being targeted for the drop</li>
31137         * <li>data - The drag data from the drag source</li>
31138         * <li>point - The point of the drop - append, above or below</li>
31139         * <li>source - The drag source</li>
31140         * <li>rawEvent - Raw mouse event</li>
31141         * <li>dropNode - Drop node(s) provided by the source.</li>
31142         * <li>cancel - Set this to true to signal drop not allowed.</li>
31143         * </ul>
31144         * @param {Object} dragOverEvent
31145         */
31146        "nodedragover" : true
31147         
31148     });
31149     if(this.singleExpand){
31150        this.on("beforeexpand", this.restrictExpand, this);
31151     }
31152     if (this.editor) {
31153         this.editor.tree = this;
31154         this.editor = Roo.factory(this.editor, Roo.tree);
31155     }
31156     
31157     if (this.selModel) {
31158         this.selModel = Roo.factory(this.selModel, Roo.tree);
31159     }
31160    
31161 };
31162 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
31163     rootVisible : true,
31164     animate: Roo.enableFx,
31165     lines : true,
31166     enableDD : false,
31167     hlDrop : Roo.enableFx,
31168   
31169     renderer: false,
31170     
31171     rendererTip: false,
31172     // private
31173     restrictExpand : function(node){
31174         var p = node.parentNode;
31175         if(p){
31176             if(p.expandedChild && p.expandedChild.parentNode == p){
31177                 p.expandedChild.collapse();
31178             }
31179             p.expandedChild = node;
31180         }
31181     },
31182
31183     // private override
31184     setRootNode : function(node){
31185         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
31186         if(!this.rootVisible){
31187             node.ui = new Roo.tree.RootTreeNodeUI(node);
31188         }
31189         return node;
31190     },
31191
31192     /**
31193      * Returns the container element for this TreePanel
31194      */
31195     getEl : function(){
31196         return this.el;
31197     },
31198
31199     /**
31200      * Returns the default TreeLoader for this TreePanel
31201      */
31202     getLoader : function(){
31203         return this.loader;
31204     },
31205
31206     /**
31207      * Expand all nodes
31208      */
31209     expandAll : function(){
31210         this.root.expand(true);
31211     },
31212
31213     /**
31214      * Collapse all nodes
31215      */
31216     collapseAll : function(){
31217         this.root.collapse(true);
31218     },
31219
31220     /**
31221      * Returns the selection model used by this TreePanel
31222      */
31223     getSelectionModel : function(){
31224         if(!this.selModel){
31225             this.selModel = new Roo.tree.DefaultSelectionModel();
31226         }
31227         return this.selModel;
31228     },
31229
31230     /**
31231      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
31232      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
31233      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
31234      * @return {Array}
31235      */
31236     getChecked : function(a, startNode){
31237         startNode = startNode || this.root;
31238         var r = [];
31239         var f = function(){
31240             if(this.attributes.checked){
31241                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
31242             }
31243         }
31244         startNode.cascade(f);
31245         return r;
31246     },
31247
31248     /**
31249      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
31250      * @param {String} path
31251      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
31252      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
31253      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
31254      */
31255     expandPath : function(path, attr, callback){
31256         attr = attr || "id";
31257         var keys = path.split(this.pathSeparator);
31258         var curNode = this.root;
31259         if(curNode.attributes[attr] != keys[1]){ // invalid root
31260             if(callback){
31261                 callback(false, null);
31262             }
31263             return;
31264         }
31265         var index = 1;
31266         var f = function(){
31267             if(++index == keys.length){
31268                 if(callback){
31269                     callback(true, curNode);
31270                 }
31271                 return;
31272             }
31273             var c = curNode.findChild(attr, keys[index]);
31274             if(!c){
31275                 if(callback){
31276                     callback(false, curNode);
31277                 }
31278                 return;
31279             }
31280             curNode = c;
31281             c.expand(false, false, f);
31282         };
31283         curNode.expand(false, false, f);
31284     },
31285
31286     /**
31287      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
31288      * @param {String} path
31289      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
31290      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
31291      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
31292      */
31293     selectPath : function(path, attr, callback){
31294         attr = attr || "id";
31295         var keys = path.split(this.pathSeparator);
31296         var v = keys.pop();
31297         if(keys.length > 0){
31298             var f = function(success, node){
31299                 if(success && node){
31300                     var n = node.findChild(attr, v);
31301                     if(n){
31302                         n.select();
31303                         if(callback){
31304                             callback(true, n);
31305                         }
31306                     }else if(callback){
31307                         callback(false, n);
31308                     }
31309                 }else{
31310                     if(callback){
31311                         callback(false, n);
31312                     }
31313                 }
31314             };
31315             this.expandPath(keys.join(this.pathSeparator), attr, f);
31316         }else{
31317             this.root.select();
31318             if(callback){
31319                 callback(true, this.root);
31320             }
31321         }
31322     },
31323
31324     getTreeEl : function(){
31325         return this.el;
31326     },
31327
31328     /**
31329      * Trigger rendering of this TreePanel
31330      */
31331     render : function(){
31332         if (this.innerCt) {
31333             return this; // stop it rendering more than once!!
31334         }
31335         
31336         this.innerCt = this.el.createChild({tag:"ul",
31337                cls:"x-tree-root-ct " +
31338                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
31339
31340         if(this.containerScroll){
31341             Roo.dd.ScrollManager.register(this.el);
31342         }
31343         if((this.enableDD || this.enableDrop) && !this.dropZone){
31344            /**
31345             * The dropZone used by this tree if drop is enabled
31346             * @type Roo.tree.TreeDropZone
31347             */
31348              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
31349                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
31350            });
31351         }
31352         if((this.enableDD || this.enableDrag) && !this.dragZone){
31353            /**
31354             * The dragZone used by this tree if drag is enabled
31355             * @type Roo.tree.TreeDragZone
31356             */
31357             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
31358                ddGroup: this.ddGroup || "TreeDD",
31359                scroll: this.ddScroll
31360            });
31361         }
31362         this.getSelectionModel().init(this);
31363         if (!this.root) {
31364             console.log("ROOT not set in tree");
31365             return;
31366         }
31367         this.root.render();
31368         if(!this.rootVisible){
31369             this.root.renderChildren();
31370         }
31371         return this;
31372     }
31373 });/*
31374  * Based on:
31375  * Ext JS Library 1.1.1
31376  * Copyright(c) 2006-2007, Ext JS, LLC.
31377  *
31378  * Originally Released Under LGPL - original licence link has changed is not relivant.
31379  *
31380  * Fork - LGPL
31381  * <script type="text/javascript">
31382  */
31383  
31384
31385 /**
31386  * @class Roo.tree.DefaultSelectionModel
31387  * @extends Roo.util.Observable
31388  * The default single selection for a TreePanel.
31389  * @param {Object} cfg Configuration
31390  */
31391 Roo.tree.DefaultSelectionModel = function(cfg){
31392    this.selNode = null;
31393    
31394    
31395    
31396    this.addEvents({
31397        /**
31398         * @event selectionchange
31399         * Fires when the selected node changes
31400         * @param {DefaultSelectionModel} this
31401         * @param {TreeNode} node the new selection
31402         */
31403        "selectionchange" : true,
31404
31405        /**
31406         * @event beforeselect
31407         * Fires before the selected node changes, return false to cancel the change
31408         * @param {DefaultSelectionModel} this
31409         * @param {TreeNode} node the new selection
31410         * @param {TreeNode} node the old selection
31411         */
31412        "beforeselect" : true
31413    });
31414    
31415     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
31416 };
31417
31418 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
31419     init : function(tree){
31420         this.tree = tree;
31421         tree.getTreeEl().on("keydown", this.onKeyDown, this);
31422         tree.on("click", this.onNodeClick, this);
31423     },
31424     
31425     onNodeClick : function(node, e){
31426         if (e.ctrlKey && this.selNode == node)  {
31427             this.unselect(node);
31428             return;
31429         }
31430         this.select(node);
31431     },
31432     
31433     /**
31434      * Select a node.
31435      * @param {TreeNode} node The node to select
31436      * @return {TreeNode} The selected node
31437      */
31438     select : function(node){
31439         var last = this.selNode;
31440         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
31441             if(last){
31442                 last.ui.onSelectedChange(false);
31443             }
31444             this.selNode = node;
31445             node.ui.onSelectedChange(true);
31446             this.fireEvent("selectionchange", this, node, last);
31447         }
31448         return node;
31449     },
31450     
31451     /**
31452      * Deselect a node.
31453      * @param {TreeNode} node The node to unselect
31454      */
31455     unselect : function(node){
31456         if(this.selNode == node){
31457             this.clearSelections();
31458         }    
31459     },
31460     
31461     /**
31462      * Clear all selections
31463      */
31464     clearSelections : function(){
31465         var n = this.selNode;
31466         if(n){
31467             n.ui.onSelectedChange(false);
31468             this.selNode = null;
31469             this.fireEvent("selectionchange", this, null);
31470         }
31471         return n;
31472     },
31473     
31474     /**
31475      * Get the selected node
31476      * @return {TreeNode} The selected node
31477      */
31478     getSelectedNode : function(){
31479         return this.selNode;    
31480     },
31481     
31482     /**
31483      * Returns true if the node is selected
31484      * @param {TreeNode} node The node to check
31485      * @return {Boolean}
31486      */
31487     isSelected : function(node){
31488         return this.selNode == node;  
31489     },
31490
31491     /**
31492      * Selects the node above the selected node in the tree, intelligently walking the nodes
31493      * @return TreeNode The new selection
31494      */
31495     selectPrevious : function(){
31496         var s = this.selNode || this.lastSelNode;
31497         if(!s){
31498             return null;
31499         }
31500         var ps = s.previousSibling;
31501         if(ps){
31502             if(!ps.isExpanded() || ps.childNodes.length < 1){
31503                 return this.select(ps);
31504             } else{
31505                 var lc = ps.lastChild;
31506                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
31507                     lc = lc.lastChild;
31508                 }
31509                 return this.select(lc);
31510             }
31511         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
31512             return this.select(s.parentNode);
31513         }
31514         return null;
31515     },
31516
31517     /**
31518      * Selects the node above the selected node in the tree, intelligently walking the nodes
31519      * @return TreeNode The new selection
31520      */
31521     selectNext : function(){
31522         var s = this.selNode || this.lastSelNode;
31523         if(!s){
31524             return null;
31525         }
31526         if(s.firstChild && s.isExpanded()){
31527              return this.select(s.firstChild);
31528          }else if(s.nextSibling){
31529              return this.select(s.nextSibling);
31530          }else if(s.parentNode){
31531             var newS = null;
31532             s.parentNode.bubble(function(){
31533                 if(this.nextSibling){
31534                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
31535                     return false;
31536                 }
31537             });
31538             return newS;
31539          }
31540         return null;
31541     },
31542
31543     onKeyDown : function(e){
31544         var s = this.selNode || this.lastSelNode;
31545         // undesirable, but required
31546         var sm = this;
31547         if(!s){
31548             return;
31549         }
31550         var k = e.getKey();
31551         switch(k){
31552              case e.DOWN:
31553                  e.stopEvent();
31554                  this.selectNext();
31555              break;
31556              case e.UP:
31557                  e.stopEvent();
31558                  this.selectPrevious();
31559              break;
31560              case e.RIGHT:
31561                  e.preventDefault();
31562                  if(s.hasChildNodes()){
31563                      if(!s.isExpanded()){
31564                          s.expand();
31565                      }else if(s.firstChild){
31566                          this.select(s.firstChild, e);
31567                      }
31568                  }
31569              break;
31570              case e.LEFT:
31571                  e.preventDefault();
31572                  if(s.hasChildNodes() && s.isExpanded()){
31573                      s.collapse();
31574                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
31575                      this.select(s.parentNode, e);
31576                  }
31577              break;
31578         };
31579     }
31580 });
31581
31582 /**
31583  * @class Roo.tree.MultiSelectionModel
31584  * @extends Roo.util.Observable
31585  * Multi selection for a TreePanel.
31586  * @param {Object} cfg Configuration
31587  */
31588 Roo.tree.MultiSelectionModel = function(){
31589    this.selNodes = [];
31590    this.selMap = {};
31591    this.addEvents({
31592        /**
31593         * @event selectionchange
31594         * Fires when the selected nodes change
31595         * @param {MultiSelectionModel} this
31596         * @param {Array} nodes Array of the selected nodes
31597         */
31598        "selectionchange" : true
31599    });
31600    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
31601    
31602 };
31603
31604 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
31605     init : function(tree){
31606         this.tree = tree;
31607         tree.getTreeEl().on("keydown", this.onKeyDown, this);
31608         tree.on("click", this.onNodeClick, this);
31609     },
31610     
31611     onNodeClick : function(node, e){
31612         this.select(node, e, e.ctrlKey);
31613     },
31614     
31615     /**
31616      * Select a node.
31617      * @param {TreeNode} node The node to select
31618      * @param {EventObject} e (optional) An event associated with the selection
31619      * @param {Boolean} keepExisting True to retain existing selections
31620      * @return {TreeNode} The selected node
31621      */
31622     select : function(node, e, keepExisting){
31623         if(keepExisting !== true){
31624             this.clearSelections(true);
31625         }
31626         if(this.isSelected(node)){
31627             this.lastSelNode = node;
31628             return node;
31629         }
31630         this.selNodes.push(node);
31631         this.selMap[node.id] = node;
31632         this.lastSelNode = node;
31633         node.ui.onSelectedChange(true);
31634         this.fireEvent("selectionchange", this, this.selNodes);
31635         return node;
31636     },
31637     
31638     /**
31639      * Deselect a node.
31640      * @param {TreeNode} node The node to unselect
31641      */
31642     unselect : function(node){
31643         if(this.selMap[node.id]){
31644             node.ui.onSelectedChange(false);
31645             var sn = this.selNodes;
31646             var index = -1;
31647             if(sn.indexOf){
31648                 index = sn.indexOf(node);
31649             }else{
31650                 for(var i = 0, len = sn.length; i < len; i++){
31651                     if(sn[i] == node){
31652                         index = i;
31653                         break;
31654                     }
31655                 }
31656             }
31657             if(index != -1){
31658                 this.selNodes.splice(index, 1);
31659             }
31660             delete this.selMap[node.id];
31661             this.fireEvent("selectionchange", this, this.selNodes);
31662         }
31663     },
31664     
31665     /**
31666      * Clear all selections
31667      */
31668     clearSelections : function(suppressEvent){
31669         var sn = this.selNodes;
31670         if(sn.length > 0){
31671             for(var i = 0, len = sn.length; i < len; i++){
31672                 sn[i].ui.onSelectedChange(false);
31673             }
31674             this.selNodes = [];
31675             this.selMap = {};
31676             if(suppressEvent !== true){
31677                 this.fireEvent("selectionchange", this, this.selNodes);
31678             }
31679         }
31680     },
31681     
31682     /**
31683      * Returns true if the node is selected
31684      * @param {TreeNode} node The node to check
31685      * @return {Boolean}
31686      */
31687     isSelected : function(node){
31688         return this.selMap[node.id] ? true : false;  
31689     },
31690     
31691     /**
31692      * Returns an array of the selected nodes
31693      * @return {Array}
31694      */
31695     getSelectedNodes : function(){
31696         return this.selNodes;    
31697     },
31698
31699     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
31700
31701     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
31702
31703     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
31704 });/*
31705  * Based on:
31706  * Ext JS Library 1.1.1
31707  * Copyright(c) 2006-2007, Ext JS, LLC.
31708  *
31709  * Originally Released Under LGPL - original licence link has changed is not relivant.
31710  *
31711  * Fork - LGPL
31712  * <script type="text/javascript">
31713  */
31714  
31715 /**
31716  * @class Roo.tree.TreeNode
31717  * @extends Roo.data.Node
31718  * @cfg {String} text The text for this node
31719  * @cfg {Boolean} expanded true to start the node expanded
31720  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
31721  * @cfg {Boolean} allowDrop false if this node cannot be drop on
31722  * @cfg {Boolean} disabled true to start the node disabled
31723  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
31724  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
31725  * @cfg {String} cls A css class to be added to the node
31726  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
31727  * @cfg {String} href URL of the link used for the node (defaults to #)
31728  * @cfg {String} hrefTarget target frame for the link
31729  * @cfg {String} qtip An Ext QuickTip for the node
31730  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
31731  * @cfg {Boolean} singleClickExpand True for single click expand on this node
31732  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
31733  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
31734  * (defaults to undefined with no checkbox rendered)
31735  * @constructor
31736  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
31737  */
31738 Roo.tree.TreeNode = function(attributes){
31739     attributes = attributes || {};
31740     if(typeof attributes == "string"){
31741         attributes = {text: attributes};
31742     }
31743     this.childrenRendered = false;
31744     this.rendered = false;
31745     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
31746     this.expanded = attributes.expanded === true;
31747     this.isTarget = attributes.isTarget !== false;
31748     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
31749     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
31750
31751     /**
31752      * Read-only. The text for this node. To change it use setText().
31753      * @type String
31754      */
31755     this.text = attributes.text;
31756     /**
31757      * True if this node is disabled.
31758      * @type Boolean
31759      */
31760     this.disabled = attributes.disabled === true;
31761
31762     this.addEvents({
31763         /**
31764         * @event textchange
31765         * Fires when the text for this node is changed
31766         * @param {Node} this This node
31767         * @param {String} text The new text
31768         * @param {String} oldText The old text
31769         */
31770         "textchange" : true,
31771         /**
31772         * @event beforeexpand
31773         * Fires before this node is expanded, return false to cancel.
31774         * @param {Node} this This node
31775         * @param {Boolean} deep
31776         * @param {Boolean} anim
31777         */
31778         "beforeexpand" : true,
31779         /**
31780         * @event beforecollapse
31781         * Fires before this node is collapsed, return false to cancel.
31782         * @param {Node} this This node
31783         * @param {Boolean} deep
31784         * @param {Boolean} anim
31785         */
31786         "beforecollapse" : true,
31787         /**
31788         * @event expand
31789         * Fires when this node is expanded
31790         * @param {Node} this This node
31791         */
31792         "expand" : true,
31793         /**
31794         * @event disabledchange
31795         * Fires when the disabled status of this node changes
31796         * @param {Node} this This node
31797         * @param {Boolean} disabled
31798         */
31799         "disabledchange" : true,
31800         /**
31801         * @event collapse
31802         * Fires when this node is collapsed
31803         * @param {Node} this This node
31804         */
31805         "collapse" : true,
31806         /**
31807         * @event beforeclick
31808         * Fires before click processing. Return false to cancel the default action.
31809         * @param {Node} this This node
31810         * @param {Roo.EventObject} e The event object
31811         */
31812         "beforeclick":true,
31813         /**
31814         * @event checkchange
31815         * Fires when a node with a checkbox's checked property changes
31816         * @param {Node} this This node
31817         * @param {Boolean} checked
31818         */
31819         "checkchange":true,
31820         /**
31821         * @event click
31822         * Fires when this node is clicked
31823         * @param {Node} this This node
31824         * @param {Roo.EventObject} e The event object
31825         */
31826         "click":true,
31827         /**
31828         * @event dblclick
31829         * Fires when this node is double clicked
31830         * @param {Node} this This node
31831         * @param {Roo.EventObject} e The event object
31832         */
31833         "dblclick":true,
31834         /**
31835         * @event contextmenu
31836         * Fires when this node is right clicked
31837         * @param {Node} this This node
31838         * @param {Roo.EventObject} e The event object
31839         */
31840         "contextmenu":true,
31841         /**
31842         * @event beforechildrenrendered
31843         * Fires right before the child nodes for this node are rendered
31844         * @param {Node} this This node
31845         */
31846         "beforechildrenrendered":true
31847     });
31848
31849     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
31850
31851     /**
31852      * Read-only. The UI for this node
31853      * @type TreeNodeUI
31854      */
31855     this.ui = new uiClass(this);
31856 };
31857 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
31858     preventHScroll: true,
31859     /**
31860      * Returns true if this node is expanded
31861      * @return {Boolean}
31862      */
31863     isExpanded : function(){
31864         return this.expanded;
31865     },
31866
31867     /**
31868      * Returns the UI object for this node
31869      * @return {TreeNodeUI}
31870      */
31871     getUI : function(){
31872         return this.ui;
31873     },
31874
31875     // private override
31876     setFirstChild : function(node){
31877         var of = this.firstChild;
31878         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
31879         if(this.childrenRendered && of && node != of){
31880             of.renderIndent(true, true);
31881         }
31882         if(this.rendered){
31883             this.renderIndent(true, true);
31884         }
31885     },
31886
31887     // private override
31888     setLastChild : function(node){
31889         var ol = this.lastChild;
31890         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
31891         if(this.childrenRendered && ol && node != ol){
31892             ol.renderIndent(true, true);
31893         }
31894         if(this.rendered){
31895             this.renderIndent(true, true);
31896         }
31897     },
31898
31899     // these methods are overridden to provide lazy rendering support
31900     // private override
31901     appendChild : function(){
31902         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
31903         if(node && this.childrenRendered){
31904             node.render();
31905         }
31906         this.ui.updateExpandIcon();
31907         return node;
31908     },
31909
31910     // private override
31911     removeChild : function(node){
31912         this.ownerTree.getSelectionModel().unselect(node);
31913         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
31914         // if it's been rendered remove dom node
31915         if(this.childrenRendered){
31916             node.ui.remove();
31917         }
31918         if(this.childNodes.length < 1){
31919             this.collapse(false, false);
31920         }else{
31921             this.ui.updateExpandIcon();
31922         }
31923         if(!this.firstChild) {
31924             this.childrenRendered = false;
31925         }
31926         return node;
31927     },
31928
31929     // private override
31930     insertBefore : function(node, refNode){
31931         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
31932         if(newNode && refNode && this.childrenRendered){
31933             node.render();
31934         }
31935         this.ui.updateExpandIcon();
31936         return newNode;
31937     },
31938
31939     /**
31940      * Sets the text for this node
31941      * @param {String} text
31942      */
31943     setText : function(text){
31944         var oldText = this.text;
31945         this.text = text;
31946         this.attributes.text = text;
31947         if(this.rendered){ // event without subscribing
31948             this.ui.onTextChange(this, text, oldText);
31949         }
31950         this.fireEvent("textchange", this, text, oldText);
31951     },
31952
31953     /**
31954      * Triggers selection of this node
31955      */
31956     select : function(){
31957         this.getOwnerTree().getSelectionModel().select(this);
31958     },
31959
31960     /**
31961      * Triggers deselection of this node
31962      */
31963     unselect : function(){
31964         this.getOwnerTree().getSelectionModel().unselect(this);
31965     },
31966
31967     /**
31968      * Returns true if this node is selected
31969      * @return {Boolean}
31970      */
31971     isSelected : function(){
31972         return this.getOwnerTree().getSelectionModel().isSelected(this);
31973     },
31974
31975     /**
31976      * Expand this node.
31977      * @param {Boolean} deep (optional) True to expand all children as well
31978      * @param {Boolean} anim (optional) false to cancel the default animation
31979      * @param {Function} callback (optional) A callback to be called when
31980      * expanding this node completes (does not wait for deep expand to complete).
31981      * Called with 1 parameter, this node.
31982      */
31983     expand : function(deep, anim, callback){
31984         if(!this.expanded){
31985             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
31986                 return;
31987             }
31988             if(!this.childrenRendered){
31989                 this.renderChildren();
31990             }
31991             this.expanded = true;
31992             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
31993                 this.ui.animExpand(function(){
31994                     this.fireEvent("expand", this);
31995                     if(typeof callback == "function"){
31996                         callback(this);
31997                     }
31998                     if(deep === true){
31999                         this.expandChildNodes(true);
32000                     }
32001                 }.createDelegate(this));
32002                 return;
32003             }else{
32004                 this.ui.expand();
32005                 this.fireEvent("expand", this);
32006                 if(typeof callback == "function"){
32007                     callback(this);
32008                 }
32009             }
32010         }else{
32011            if(typeof callback == "function"){
32012                callback(this);
32013            }
32014         }
32015         if(deep === true){
32016             this.expandChildNodes(true);
32017         }
32018     },
32019
32020     isHiddenRoot : function(){
32021         return this.isRoot && !this.getOwnerTree().rootVisible;
32022     },
32023
32024     /**
32025      * Collapse this node.
32026      * @param {Boolean} deep (optional) True to collapse all children as well
32027      * @param {Boolean} anim (optional) false to cancel the default animation
32028      */
32029     collapse : function(deep, anim){
32030         if(this.expanded && !this.isHiddenRoot()){
32031             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
32032                 return;
32033             }
32034             this.expanded = false;
32035             if((this.getOwnerTree().animate && anim !== false) || anim){
32036                 this.ui.animCollapse(function(){
32037                     this.fireEvent("collapse", this);
32038                     if(deep === true){
32039                         this.collapseChildNodes(true);
32040                     }
32041                 }.createDelegate(this));
32042                 return;
32043             }else{
32044                 this.ui.collapse();
32045                 this.fireEvent("collapse", this);
32046             }
32047         }
32048         if(deep === true){
32049             var cs = this.childNodes;
32050             for(var i = 0, len = cs.length; i < len; i++) {
32051                 cs[i].collapse(true, false);
32052             }
32053         }
32054     },
32055
32056     // private
32057     delayedExpand : function(delay){
32058         if(!this.expandProcId){
32059             this.expandProcId = this.expand.defer(delay, this);
32060         }
32061     },
32062
32063     // private
32064     cancelExpand : function(){
32065         if(this.expandProcId){
32066             clearTimeout(this.expandProcId);
32067         }
32068         this.expandProcId = false;
32069     },
32070
32071     /**
32072      * Toggles expanded/collapsed state of the node
32073      */
32074     toggle : function(){
32075         if(this.expanded){
32076             this.collapse();
32077         }else{
32078             this.expand();
32079         }
32080     },
32081
32082     /**
32083      * Ensures all parent nodes are expanded
32084      */
32085     ensureVisible : function(callback){
32086         var tree = this.getOwnerTree();
32087         tree.expandPath(this.parentNode.getPath(), false, function(){
32088             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
32089             Roo.callback(callback);
32090         }.createDelegate(this));
32091     },
32092
32093     /**
32094      * Expand all child nodes
32095      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
32096      */
32097     expandChildNodes : function(deep){
32098         var cs = this.childNodes;
32099         for(var i = 0, len = cs.length; i < len; i++) {
32100                 cs[i].expand(deep);
32101         }
32102     },
32103
32104     /**
32105      * Collapse all child nodes
32106      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
32107      */
32108     collapseChildNodes : function(deep){
32109         var cs = this.childNodes;
32110         for(var i = 0, len = cs.length; i < len; i++) {
32111                 cs[i].collapse(deep);
32112         }
32113     },
32114
32115     /**
32116      * Disables this node
32117      */
32118     disable : function(){
32119         this.disabled = true;
32120         this.unselect();
32121         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
32122             this.ui.onDisableChange(this, true);
32123         }
32124         this.fireEvent("disabledchange", this, true);
32125     },
32126
32127     /**
32128      * Enables this node
32129      */
32130     enable : function(){
32131         this.disabled = false;
32132         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
32133             this.ui.onDisableChange(this, false);
32134         }
32135         this.fireEvent("disabledchange", this, false);
32136     },
32137
32138     // private
32139     renderChildren : function(suppressEvent){
32140         if(suppressEvent !== false){
32141             this.fireEvent("beforechildrenrendered", this);
32142         }
32143         var cs = this.childNodes;
32144         for(var i = 0, len = cs.length; i < len; i++){
32145             cs[i].render(true);
32146         }
32147         this.childrenRendered = true;
32148     },
32149
32150     // private
32151     sort : function(fn, scope){
32152         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
32153         if(this.childrenRendered){
32154             var cs = this.childNodes;
32155             for(var i = 0, len = cs.length; i < len; i++){
32156                 cs[i].render(true);
32157             }
32158         }
32159     },
32160
32161     // private
32162     render : function(bulkRender){
32163         this.ui.render(bulkRender);
32164         if(!this.rendered){
32165             this.rendered = true;
32166             if(this.expanded){
32167                 this.expanded = false;
32168                 this.expand(false, false);
32169             }
32170         }
32171     },
32172
32173     // private
32174     renderIndent : function(deep, refresh){
32175         if(refresh){
32176             this.ui.childIndent = null;
32177         }
32178         this.ui.renderIndent();
32179         if(deep === true && this.childrenRendered){
32180             var cs = this.childNodes;
32181             for(var i = 0, len = cs.length; i < len; i++){
32182                 cs[i].renderIndent(true, refresh);
32183             }
32184         }
32185     }
32186 });/*
32187  * Based on:
32188  * Ext JS Library 1.1.1
32189  * Copyright(c) 2006-2007, Ext JS, LLC.
32190  *
32191  * Originally Released Under LGPL - original licence link has changed is not relivant.
32192  *
32193  * Fork - LGPL
32194  * <script type="text/javascript">
32195  */
32196  
32197 /**
32198  * @class Roo.tree.AsyncTreeNode
32199  * @extends Roo.tree.TreeNode
32200  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
32201  * @constructor
32202  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
32203  */
32204  Roo.tree.AsyncTreeNode = function(config){
32205     this.loaded = false;
32206     this.loading = false;
32207     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
32208     /**
32209     * @event beforeload
32210     * Fires before this node is loaded, return false to cancel
32211     * @param {Node} this This node
32212     */
32213     this.addEvents({'beforeload':true, 'load': true});
32214     /**
32215     * @event load
32216     * Fires when this node is loaded
32217     * @param {Node} this This node
32218     */
32219     /**
32220      * The loader used by this node (defaults to using the tree's defined loader)
32221      * @type TreeLoader
32222      * @property loader
32223      */
32224 };
32225 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
32226     expand : function(deep, anim, callback){
32227         if(this.loading){ // if an async load is already running, waiting til it's done
32228             var timer;
32229             var f = function(){
32230                 if(!this.loading){ // done loading
32231                     clearInterval(timer);
32232                     this.expand(deep, anim, callback);
32233                 }
32234             }.createDelegate(this);
32235             timer = setInterval(f, 200);
32236             return;
32237         }
32238         if(!this.loaded){
32239             if(this.fireEvent("beforeload", this) === false){
32240                 return;
32241             }
32242             this.loading = true;
32243             this.ui.beforeLoad(this);
32244             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
32245             if(loader){
32246                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
32247                 return;
32248             }
32249         }
32250         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
32251     },
32252     
32253     /**
32254      * Returns true if this node is currently loading
32255      * @return {Boolean}
32256      */
32257     isLoading : function(){
32258         return this.loading;  
32259     },
32260     
32261     loadComplete : function(deep, anim, callback){
32262         this.loading = false;
32263         this.loaded = true;
32264         this.ui.afterLoad(this);
32265         this.fireEvent("load", this);
32266         this.expand(deep, anim, callback);
32267     },
32268     
32269     /**
32270      * Returns true if this node has been loaded
32271      * @return {Boolean}
32272      */
32273     isLoaded : function(){
32274         return this.loaded;
32275     },
32276     
32277     hasChildNodes : function(){
32278         if(!this.isLeaf() && !this.loaded){
32279             return true;
32280         }else{
32281             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
32282         }
32283     },
32284
32285     /**
32286      * Trigger a reload for this node
32287      * @param {Function} callback
32288      */
32289     reload : function(callback){
32290         this.collapse(false, false);
32291         while(this.firstChild){
32292             this.removeChild(this.firstChild);
32293         }
32294         this.childrenRendered = false;
32295         this.loaded = false;
32296         if(this.isHiddenRoot()){
32297             this.expanded = false;
32298         }
32299         this.expand(false, false, callback);
32300     }
32301 });/*
32302  * Based on:
32303  * Ext JS Library 1.1.1
32304  * Copyright(c) 2006-2007, Ext JS, LLC.
32305  *
32306  * Originally Released Under LGPL - original licence link has changed is not relivant.
32307  *
32308  * Fork - LGPL
32309  * <script type="text/javascript">
32310  */
32311  
32312 /**
32313  * @class Roo.tree.TreeNodeUI
32314  * @constructor
32315  * @param {Object} node The node to render
32316  * The TreeNode UI implementation is separate from the
32317  * tree implementation. Unless you are customizing the tree UI,
32318  * you should never have to use this directly.
32319  */
32320 Roo.tree.TreeNodeUI = function(node){
32321     this.node = node;
32322     this.rendered = false;
32323     this.animating = false;
32324     this.emptyIcon = Roo.BLANK_IMAGE_URL;
32325 };
32326
32327 Roo.tree.TreeNodeUI.prototype = {
32328     removeChild : function(node){
32329         if(this.rendered){
32330             this.ctNode.removeChild(node.ui.getEl());
32331         }
32332     },
32333
32334     beforeLoad : function(){
32335          this.addClass("x-tree-node-loading");
32336     },
32337
32338     afterLoad : function(){
32339          this.removeClass("x-tree-node-loading");
32340     },
32341
32342     onTextChange : function(node, text, oldText){
32343         if(this.rendered){
32344             this.textNode.innerHTML = text;
32345         }
32346     },
32347
32348     onDisableChange : function(node, state){
32349         this.disabled = state;
32350         if(state){
32351             this.addClass("x-tree-node-disabled");
32352         }else{
32353             this.removeClass("x-tree-node-disabled");
32354         }
32355     },
32356
32357     onSelectedChange : function(state){
32358         if(state){
32359             this.focus();
32360             this.addClass("x-tree-selected");
32361         }else{
32362             //this.blur();
32363             this.removeClass("x-tree-selected");
32364         }
32365     },
32366
32367     onMove : function(tree, node, oldParent, newParent, index, refNode){
32368         this.childIndent = null;
32369         if(this.rendered){
32370             var targetNode = newParent.ui.getContainer();
32371             if(!targetNode){//target not rendered
32372                 this.holder = document.createElement("div");
32373                 this.holder.appendChild(this.wrap);
32374                 return;
32375             }
32376             var insertBefore = refNode ? refNode.ui.getEl() : null;
32377             if(insertBefore){
32378                 targetNode.insertBefore(this.wrap, insertBefore);
32379             }else{
32380                 targetNode.appendChild(this.wrap);
32381             }
32382             this.node.renderIndent(true);
32383         }
32384     },
32385
32386     addClass : function(cls){
32387         if(this.elNode){
32388             Roo.fly(this.elNode).addClass(cls);
32389         }
32390     },
32391
32392     removeClass : function(cls){
32393         if(this.elNode){
32394             Roo.fly(this.elNode).removeClass(cls);
32395         }
32396     },
32397
32398     remove : function(){
32399         if(this.rendered){
32400             this.holder = document.createElement("div");
32401             this.holder.appendChild(this.wrap);
32402         }
32403     },
32404
32405     fireEvent : function(){
32406         return this.node.fireEvent.apply(this.node, arguments);
32407     },
32408
32409     initEvents : function(){
32410         this.node.on("move", this.onMove, this);
32411         var E = Roo.EventManager;
32412         var a = this.anchor;
32413
32414         var el = Roo.fly(a, '_treeui');
32415
32416         if(Roo.isOpera){ // opera render bug ignores the CSS
32417             el.setStyle("text-decoration", "none");
32418         }
32419
32420         el.on("click", this.onClick, this);
32421         el.on("dblclick", this.onDblClick, this);
32422
32423         if(this.checkbox){
32424             Roo.EventManager.on(this.checkbox,
32425                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
32426         }
32427
32428         el.on("contextmenu", this.onContextMenu, this);
32429
32430         var icon = Roo.fly(this.iconNode);
32431         icon.on("click", this.onClick, this);
32432         icon.on("dblclick", this.onDblClick, this);
32433         icon.on("contextmenu", this.onContextMenu, this);
32434         E.on(this.ecNode, "click", this.ecClick, this, true);
32435
32436         if(this.node.disabled){
32437             this.addClass("x-tree-node-disabled");
32438         }
32439         if(this.node.hidden){
32440             this.addClass("x-tree-node-disabled");
32441         }
32442         var ot = this.node.getOwnerTree();
32443         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
32444         if(dd && (!this.node.isRoot || ot.rootVisible)){
32445             Roo.dd.Registry.register(this.elNode, {
32446                 node: this.node,
32447                 handles: this.getDDHandles(),
32448                 isHandle: false
32449             });
32450         }
32451     },
32452
32453     getDDHandles : function(){
32454         return [this.iconNode, this.textNode];
32455     },
32456
32457     hide : function(){
32458         if(this.rendered){
32459             this.wrap.style.display = "none";
32460         }
32461     },
32462
32463     show : function(){
32464         if(this.rendered){
32465             this.wrap.style.display = "";
32466         }
32467     },
32468
32469     onContextMenu : function(e){
32470         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
32471             e.preventDefault();
32472             this.focus();
32473             this.fireEvent("contextmenu", this.node, e);
32474         }
32475     },
32476
32477     onClick : function(e){
32478         if(this.dropping){
32479             e.stopEvent();
32480             return;
32481         }
32482         if(this.fireEvent("beforeclick", this.node, e) !== false){
32483             if(!this.disabled && this.node.attributes.href){
32484                 this.fireEvent("click", this.node, e);
32485                 return;
32486             }
32487             e.preventDefault();
32488             if(this.disabled){
32489                 return;
32490             }
32491
32492             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
32493                 this.node.toggle();
32494             }
32495
32496             this.fireEvent("click", this.node, e);
32497         }else{
32498             e.stopEvent();
32499         }
32500     },
32501
32502     onDblClick : function(e){
32503         e.preventDefault();
32504         if(this.disabled){
32505             return;
32506         }
32507         if(this.checkbox){
32508             this.toggleCheck();
32509         }
32510         if(!this.animating && this.node.hasChildNodes()){
32511             this.node.toggle();
32512         }
32513         this.fireEvent("dblclick", this.node, e);
32514     },
32515
32516     onCheckChange : function(){
32517         var checked = this.checkbox.checked;
32518         this.node.attributes.checked = checked;
32519         this.fireEvent('checkchange', this.node, checked);
32520     },
32521
32522     ecClick : function(e){
32523         if(!this.animating && this.node.hasChildNodes()){
32524             this.node.toggle();
32525         }
32526     },
32527
32528     startDrop : function(){
32529         this.dropping = true;
32530     },
32531
32532     // delayed drop so the click event doesn't get fired on a drop
32533     endDrop : function(){
32534        setTimeout(function(){
32535            this.dropping = false;
32536        }.createDelegate(this), 50);
32537     },
32538
32539     expand : function(){
32540         this.updateExpandIcon();
32541         this.ctNode.style.display = "";
32542     },
32543
32544     focus : function(){
32545         if(!this.node.preventHScroll){
32546             try{this.anchor.focus();
32547             }catch(e){}
32548         }else if(!Roo.isIE){
32549             try{
32550                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
32551                 var l = noscroll.scrollLeft;
32552                 this.anchor.focus();
32553                 noscroll.scrollLeft = l;
32554             }catch(e){}
32555         }
32556     },
32557
32558     toggleCheck : function(value){
32559         var cb = this.checkbox;
32560         if(cb){
32561             cb.checked = (value === undefined ? !cb.checked : value);
32562         }
32563     },
32564
32565     blur : function(){
32566         try{
32567             this.anchor.blur();
32568         }catch(e){}
32569     },
32570
32571     animExpand : function(callback){
32572         var ct = Roo.get(this.ctNode);
32573         ct.stopFx();
32574         if(!this.node.hasChildNodes()){
32575             this.updateExpandIcon();
32576             this.ctNode.style.display = "";
32577             Roo.callback(callback);
32578             return;
32579         }
32580         this.animating = true;
32581         this.updateExpandIcon();
32582
32583         ct.slideIn('t', {
32584            callback : function(){
32585                this.animating = false;
32586                Roo.callback(callback);
32587             },
32588             scope: this,
32589             duration: this.node.ownerTree.duration || .25
32590         });
32591     },
32592
32593     highlight : function(){
32594         var tree = this.node.getOwnerTree();
32595         Roo.fly(this.wrap).highlight(
32596             tree.hlColor || "C3DAF9",
32597             {endColor: tree.hlBaseColor}
32598         );
32599     },
32600
32601     collapse : function(){
32602         this.updateExpandIcon();
32603         this.ctNode.style.display = "none";
32604     },
32605
32606     animCollapse : function(callback){
32607         var ct = Roo.get(this.ctNode);
32608         ct.enableDisplayMode('block');
32609         ct.stopFx();
32610
32611         this.animating = true;
32612         this.updateExpandIcon();
32613
32614         ct.slideOut('t', {
32615             callback : function(){
32616                this.animating = false;
32617                Roo.callback(callback);
32618             },
32619             scope: this,
32620             duration: this.node.ownerTree.duration || .25
32621         });
32622     },
32623
32624     getContainer : function(){
32625         return this.ctNode;
32626     },
32627
32628     getEl : function(){
32629         return this.wrap;
32630     },
32631
32632     appendDDGhost : function(ghostNode){
32633         ghostNode.appendChild(this.elNode.cloneNode(true));
32634     },
32635
32636     getDDRepairXY : function(){
32637         return Roo.lib.Dom.getXY(this.iconNode);
32638     },
32639
32640     onRender : function(){
32641         this.render();
32642     },
32643
32644     render : function(bulkRender){
32645         var n = this.node, a = n.attributes;
32646         var targetNode = n.parentNode ?
32647               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
32648
32649         if(!this.rendered){
32650             this.rendered = true;
32651
32652             this.renderElements(n, a, targetNode, bulkRender);
32653
32654             if(a.qtip){
32655                if(this.textNode.setAttributeNS){
32656                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
32657                    if(a.qtipTitle){
32658                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
32659                    }
32660                }else{
32661                    this.textNode.setAttribute("ext:qtip", a.qtip);
32662                    if(a.qtipTitle){
32663                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
32664                    }
32665                }
32666             }else if(a.qtipCfg){
32667                 a.qtipCfg.target = Roo.id(this.textNode);
32668                 Roo.QuickTips.register(a.qtipCfg);
32669             }
32670             this.initEvents();
32671             if(!this.node.expanded){
32672                 this.updateExpandIcon();
32673             }
32674         }else{
32675             if(bulkRender === true) {
32676                 targetNode.appendChild(this.wrap);
32677             }
32678         }
32679     },
32680
32681     renderElements : function(n, a, targetNode, bulkRender)
32682     {
32683         // add some indent caching, this helps performance when rendering a large tree
32684         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
32685         var t = n.getOwnerTree();
32686         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
32687         if (typeof(n.attributes.html) != 'undefined') {
32688             txt = n.attributes.html;
32689         }
32690         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
32691         var cb = typeof a.checked == 'boolean';
32692         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
32693         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
32694             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
32695             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
32696             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
32697             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
32698             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
32699              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
32700                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
32701             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
32702             "</li>"];
32703
32704         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
32705             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
32706                                 n.nextSibling.ui.getEl(), buf.join(""));
32707         }else{
32708             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
32709         }
32710
32711         this.elNode = this.wrap.childNodes[0];
32712         this.ctNode = this.wrap.childNodes[1];
32713         var cs = this.elNode.childNodes;
32714         this.indentNode = cs[0];
32715         this.ecNode = cs[1];
32716         this.iconNode = cs[2];
32717         var index = 3;
32718         if(cb){
32719             this.checkbox = cs[3];
32720             index++;
32721         }
32722         this.anchor = cs[index];
32723         this.textNode = cs[index].firstChild;
32724     },
32725
32726     getAnchor : function(){
32727         return this.anchor;
32728     },
32729
32730     getTextEl : function(){
32731         return this.textNode;
32732     },
32733
32734     getIconEl : function(){
32735         return this.iconNode;
32736     },
32737
32738     isChecked : function(){
32739         return this.checkbox ? this.checkbox.checked : false;
32740     },
32741
32742     updateExpandIcon : function(){
32743         if(this.rendered){
32744             var n = this.node, c1, c2;
32745             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
32746             var hasChild = n.hasChildNodes();
32747             if(hasChild){
32748                 if(n.expanded){
32749                     cls += "-minus";
32750                     c1 = "x-tree-node-collapsed";
32751                     c2 = "x-tree-node-expanded";
32752                 }else{
32753                     cls += "-plus";
32754                     c1 = "x-tree-node-expanded";
32755                     c2 = "x-tree-node-collapsed";
32756                 }
32757                 if(this.wasLeaf){
32758                     this.removeClass("x-tree-node-leaf");
32759                     this.wasLeaf = false;
32760                 }
32761                 if(this.c1 != c1 || this.c2 != c2){
32762                     Roo.fly(this.elNode).replaceClass(c1, c2);
32763                     this.c1 = c1; this.c2 = c2;
32764                 }
32765             }else{
32766                 // this changes non-leafs into leafs if they have no children.
32767                 // it's not very rational behaviour..
32768                 
32769                 if(!this.wasLeaf && this.node.leaf){
32770                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
32771                     delete this.c1;
32772                     delete this.c2;
32773                     this.wasLeaf = true;
32774                 }
32775             }
32776             var ecc = "x-tree-ec-icon "+cls;
32777             if(this.ecc != ecc){
32778                 this.ecNode.className = ecc;
32779                 this.ecc = ecc;
32780             }
32781         }
32782     },
32783
32784     getChildIndent : function(){
32785         if(!this.childIndent){
32786             var buf = [];
32787             var p = this.node;
32788             while(p){
32789                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
32790                     if(!p.isLast()) {
32791                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
32792                     } else {
32793                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
32794                     }
32795                 }
32796                 p = p.parentNode;
32797             }
32798             this.childIndent = buf.join("");
32799         }
32800         return this.childIndent;
32801     },
32802
32803     renderIndent : function(){
32804         if(this.rendered){
32805             var indent = "";
32806             var p = this.node.parentNode;
32807             if(p){
32808                 indent = p.ui.getChildIndent();
32809             }
32810             if(this.indentMarkup != indent){ // don't rerender if not required
32811                 this.indentNode.innerHTML = indent;
32812                 this.indentMarkup = indent;
32813             }
32814             this.updateExpandIcon();
32815         }
32816     }
32817 };
32818
32819 Roo.tree.RootTreeNodeUI = function(){
32820     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
32821 };
32822 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
32823     render : function(){
32824         if(!this.rendered){
32825             var targetNode = this.node.ownerTree.innerCt.dom;
32826             this.node.expanded = true;
32827             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
32828             this.wrap = this.ctNode = targetNode.firstChild;
32829         }
32830     },
32831     collapse : function(){
32832     },
32833     expand : function(){
32834     }
32835 });/*
32836  * Based on:
32837  * Ext JS Library 1.1.1
32838  * Copyright(c) 2006-2007, Ext JS, LLC.
32839  *
32840  * Originally Released Under LGPL - original licence link has changed is not relivant.
32841  *
32842  * Fork - LGPL
32843  * <script type="text/javascript">
32844  */
32845 /**
32846  * @class Roo.tree.TreeLoader
32847  * @extends Roo.util.Observable
32848  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
32849  * nodes from a specified URL. The response must be a javascript Array definition
32850  * who's elements are node definition objects. eg:
32851  * <pre><code>
32852    [{ 'id': 1, 'text': 'A folder Node', 'leaf': false },
32853     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }]
32854 </code></pre>
32855  * <br><br>
32856  * A server request is sent, and child nodes are loaded only when a node is expanded.
32857  * The loading node's id is passed to the server under the parameter name "node" to
32858  * enable the server to produce the correct child nodes.
32859  * <br><br>
32860  * To pass extra parameters, an event handler may be attached to the "beforeload"
32861  * event, and the parameters specified in the TreeLoader's baseParams property:
32862  * <pre><code>
32863     myTreeLoader.on("beforeload", function(treeLoader, node) {
32864         this.baseParams.category = node.attributes.category;
32865     }, this);
32866 </code></pre><
32867  * This would pass an HTTP parameter called "category" to the server containing
32868  * the value of the Node's "category" attribute.
32869  * @constructor
32870  * Creates a new Treeloader.
32871  * @param {Object} config A config object containing config properties.
32872  */
32873 Roo.tree.TreeLoader = function(config){
32874     this.baseParams = {};
32875     this.requestMethod = "POST";
32876     Roo.apply(this, config);
32877
32878     this.addEvents({
32879     
32880         /**
32881          * @event beforeload
32882          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
32883          * @param {Object} This TreeLoader object.
32884          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32885          * @param {Object} callback The callback function specified in the {@link #load} call.
32886          */
32887         beforeload : true,
32888         /**
32889          * @event load
32890          * Fires when the node has been successfuly loaded.
32891          * @param {Object} This TreeLoader object.
32892          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32893          * @param {Object} response The response object containing the data from the server.
32894          */
32895         load : true,
32896         /**
32897          * @event loadexception
32898          * Fires if the network request failed.
32899          * @param {Object} This TreeLoader object.
32900          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32901          * @param {Object} response The response object containing the data from the server.
32902          */
32903         loadexception : true,
32904         /**
32905          * @event create
32906          * Fires before a node is created, enabling you to return custom Node types 
32907          * @param {Object} This TreeLoader object.
32908          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
32909          */
32910         create : true
32911     });
32912
32913     Roo.tree.TreeLoader.superclass.constructor.call(this);
32914 };
32915
32916 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
32917     /**
32918     * @cfg {String} dataUrl The URL from which to request a Json string which
32919     * specifies an array of node definition object representing the child nodes
32920     * to be loaded.
32921     */
32922     /**
32923     * @cfg {Object} baseParams (optional) An object containing properties which
32924     * specify HTTP parameters to be passed to each request for child nodes.
32925     */
32926     /**
32927     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
32928     * created by this loader. If the attributes sent by the server have an attribute in this object,
32929     * they take priority.
32930     */
32931     /**
32932     * @cfg {Object} uiProviders (optional) An object containing properties which
32933     * 
32934     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
32935     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
32936     * <i>uiProvider</i> attribute of a returned child node is a string rather
32937     * than a reference to a TreeNodeUI implementation, this that string value
32938     * is used as a property name in the uiProviders object. You can define the provider named
32939     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
32940     */
32941     uiProviders : {},
32942
32943     /**
32944     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
32945     * child nodes before loading.
32946     */
32947     clearOnLoad : true,
32948
32949     /**
32950     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
32951     * property on loading, rather than expecting an array. (eg. more compatible to a standard
32952     * Grid query { data : [ .....] }
32953     */
32954     
32955     root : false,
32956      /**
32957     * @cfg {String} queryParam (optional) 
32958     * Name of the query as it will be passed on the querystring (defaults to 'node')
32959     * eg. the request will be ?node=[id]
32960     */
32961     
32962     
32963     queryParam: false,
32964     
32965     /**
32966      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
32967      * This is called automatically when a node is expanded, but may be used to reload
32968      * a node (or append new children if the {@link #clearOnLoad} option is false.)
32969      * @param {Roo.tree.TreeNode} node
32970      * @param {Function} callback
32971      */
32972     load : function(node, callback){
32973         if(this.clearOnLoad){
32974             while(node.firstChild){
32975                 node.removeChild(node.firstChild);
32976             }
32977         }
32978         if(node.attributes.children){ // preloaded json children
32979             var cs = node.attributes.children;
32980             for(var i = 0, len = cs.length; i < len; i++){
32981                 node.appendChild(this.createNode(cs[i]));
32982             }
32983             if(typeof callback == "function"){
32984                 callback();
32985             }
32986         }else if(this.dataUrl){
32987             this.requestData(node, callback);
32988         }
32989     },
32990
32991     getParams: function(node){
32992         var buf = [], bp = this.baseParams;
32993         for(var key in bp){
32994             if(typeof bp[key] != "function"){
32995                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
32996             }
32997         }
32998         var n = this.queryParam === false ? 'node' : this.queryParam;
32999         buf.push(n + "=", encodeURIComponent(node.id));
33000         return buf.join("");
33001     },
33002
33003     requestData : function(node, callback){
33004         if(this.fireEvent("beforeload", this, node, callback) !== false){
33005             this.transId = Roo.Ajax.request({
33006                 method:this.requestMethod,
33007                 url: this.dataUrl||this.url,
33008                 success: this.handleResponse,
33009                 failure: this.handleFailure,
33010                 scope: this,
33011                 argument: {callback: callback, node: node},
33012                 params: this.getParams(node)
33013             });
33014         }else{
33015             // if the load is cancelled, make sure we notify
33016             // the node that we are done
33017             if(typeof callback == "function"){
33018                 callback();
33019             }
33020         }
33021     },
33022
33023     isLoading : function(){
33024         return this.transId ? true : false;
33025     },
33026
33027     abort : function(){
33028         if(this.isLoading()){
33029             Roo.Ajax.abort(this.transId);
33030         }
33031     },
33032
33033     // private
33034     createNode : function(attr)
33035     {
33036         // apply baseAttrs, nice idea Corey!
33037         if(this.baseAttrs){
33038             Roo.applyIf(attr, this.baseAttrs);
33039         }
33040         if(this.applyLoader !== false){
33041             attr.loader = this;
33042         }
33043         // uiProvider = depreciated..
33044         
33045         if(typeof(attr.uiProvider) == 'string'){
33046            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
33047                 /**  eval:var:attr */ eval(attr.uiProvider);
33048         }
33049         if(typeof(this.uiProviders['default']) != 'undefined') {
33050             attr.uiProvider = this.uiProviders['default'];
33051         }
33052         
33053         this.fireEvent('create', this, attr);
33054         
33055         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
33056         return(attr.leaf ?
33057                         new Roo.tree.TreeNode(attr) :
33058                         new Roo.tree.AsyncTreeNode(attr));
33059     },
33060
33061     processResponse : function(response, node, callback)
33062     {
33063         var json = response.responseText;
33064         try {
33065             
33066             var o = Roo.decode(json);
33067             
33068             if (!o.success) {
33069                 // it's a failure condition.
33070                 var a = response.argument;
33071                 this.fireEvent("loadexception", this, a.node, response);
33072                 Roo.log("Load failed - should have a handler really");
33073                 return;
33074             }
33075             
33076             if (this.root !== false) {
33077                 o = o[this.root];
33078             }
33079             
33080             for(var i = 0, len = o.length; i < len; i++){
33081                 var n = this.createNode(o[i]);
33082                 if(n){
33083                     node.appendChild(n);
33084                 }
33085             }
33086             if(typeof callback == "function"){
33087                 callback(this, node);
33088             }
33089         }catch(e){
33090             this.handleFailure(response);
33091         }
33092     },
33093
33094     handleResponse : function(response){
33095         this.transId = false;
33096         var a = response.argument;
33097         this.processResponse(response, a.node, a.callback);
33098         this.fireEvent("load", this, a.node, response);
33099     },
33100
33101     handleFailure : function(response)
33102     {
33103         // should handle failure better..
33104         this.transId = false;
33105         var a = response.argument;
33106         this.fireEvent("loadexception", this, a.node, response);
33107         if(typeof a.callback == "function"){
33108             a.callback(this, a.node);
33109         }
33110     }
33111 });/*
33112  * Based on:
33113  * Ext JS Library 1.1.1
33114  * Copyright(c) 2006-2007, Ext JS, LLC.
33115  *
33116  * Originally Released Under LGPL - original licence link has changed is not relivant.
33117  *
33118  * Fork - LGPL
33119  * <script type="text/javascript">
33120  */
33121
33122 /**
33123 * @class Roo.tree.TreeFilter
33124 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
33125 * @param {TreePanel} tree
33126 * @param {Object} config (optional)
33127  */
33128 Roo.tree.TreeFilter = function(tree, config){
33129     this.tree = tree;
33130     this.filtered = {};
33131     Roo.apply(this, config);
33132 };
33133
33134 Roo.tree.TreeFilter.prototype = {
33135     clearBlank:false,
33136     reverse:false,
33137     autoClear:false,
33138     remove:false,
33139
33140      /**
33141      * Filter the data by a specific attribute.
33142      * @param {String/RegExp} value Either string that the attribute value
33143      * should start with or a RegExp to test against the attribute
33144      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
33145      * @param {TreeNode} startNode (optional) The node to start the filter at.
33146      */
33147     filter : function(value, attr, startNode){
33148         attr = attr || "text";
33149         var f;
33150         if(typeof value == "string"){
33151             var vlen = value.length;
33152             // auto clear empty filter
33153             if(vlen == 0 && this.clearBlank){
33154                 this.clear();
33155                 return;
33156             }
33157             value = value.toLowerCase();
33158             f = function(n){
33159                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
33160             };
33161         }else if(value.exec){ // regex?
33162             f = function(n){
33163                 return value.test(n.attributes[attr]);
33164             };
33165         }else{
33166             throw 'Illegal filter type, must be string or regex';
33167         }
33168         this.filterBy(f, null, startNode);
33169         },
33170
33171     /**
33172      * Filter by a function. The passed function will be called with each
33173      * node in the tree (or from the startNode). If the function returns true, the node is kept
33174      * otherwise it is filtered. If a node is filtered, its children are also filtered.
33175      * @param {Function} fn The filter function
33176      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
33177      */
33178     filterBy : function(fn, scope, startNode){
33179         startNode = startNode || this.tree.root;
33180         if(this.autoClear){
33181             this.clear();
33182         }
33183         var af = this.filtered, rv = this.reverse;
33184         var f = function(n){
33185             if(n == startNode){
33186                 return true;
33187             }
33188             if(af[n.id]){
33189                 return false;
33190             }
33191             var m = fn.call(scope || n, n);
33192             if(!m || rv){
33193                 af[n.id] = n;
33194                 n.ui.hide();
33195                 return false;
33196             }
33197             return true;
33198         };
33199         startNode.cascade(f);
33200         if(this.remove){
33201            for(var id in af){
33202                if(typeof id != "function"){
33203                    var n = af[id];
33204                    if(n && n.parentNode){
33205                        n.parentNode.removeChild(n);
33206                    }
33207                }
33208            }
33209         }
33210     },
33211
33212     /**
33213      * Clears the current filter. Note: with the "remove" option
33214      * set a filter cannot be cleared.
33215      */
33216     clear : function(){
33217         var t = this.tree;
33218         var af = this.filtered;
33219         for(var id in af){
33220             if(typeof id != "function"){
33221                 var n = af[id];
33222                 if(n){
33223                     n.ui.show();
33224                 }
33225             }
33226         }
33227         this.filtered = {};
33228     }
33229 };
33230 /*
33231  * Based on:
33232  * Ext JS Library 1.1.1
33233  * Copyright(c) 2006-2007, Ext JS, LLC.
33234  *
33235  * Originally Released Under LGPL - original licence link has changed is not relivant.
33236  *
33237  * Fork - LGPL
33238  * <script type="text/javascript">
33239  */
33240  
33241
33242 /**
33243  * @class Roo.tree.TreeSorter
33244  * Provides sorting of nodes in a TreePanel
33245  * 
33246  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
33247  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
33248  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
33249  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
33250  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
33251  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
33252  * @constructor
33253  * @param {TreePanel} tree
33254  * @param {Object} config
33255  */
33256 Roo.tree.TreeSorter = function(tree, config){
33257     Roo.apply(this, config);
33258     tree.on("beforechildrenrendered", this.doSort, this);
33259     tree.on("append", this.updateSort, this);
33260     tree.on("insert", this.updateSort, this);
33261     
33262     var dsc = this.dir && this.dir.toLowerCase() == "desc";
33263     var p = this.property || "text";
33264     var sortType = this.sortType;
33265     var fs = this.folderSort;
33266     var cs = this.caseSensitive === true;
33267     var leafAttr = this.leafAttr || 'leaf';
33268
33269     this.sortFn = function(n1, n2){
33270         if(fs){
33271             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
33272                 return 1;
33273             }
33274             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
33275                 return -1;
33276             }
33277         }
33278         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
33279         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
33280         if(v1 < v2){
33281                         return dsc ? +1 : -1;
33282                 }else if(v1 > v2){
33283                         return dsc ? -1 : +1;
33284         }else{
33285                 return 0;
33286         }
33287     };
33288 };
33289
33290 Roo.tree.TreeSorter.prototype = {
33291     doSort : function(node){
33292         node.sort(this.sortFn);
33293     },
33294     
33295     compareNodes : function(n1, n2){
33296         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
33297     },
33298     
33299     updateSort : function(tree, node){
33300         if(node.childrenRendered){
33301             this.doSort.defer(1, this, [node]);
33302         }
33303     }
33304 };/*
33305  * Based on:
33306  * Ext JS Library 1.1.1
33307  * Copyright(c) 2006-2007, Ext JS, LLC.
33308  *
33309  * Originally Released Under LGPL - original licence link has changed is not relivant.
33310  *
33311  * Fork - LGPL
33312  * <script type="text/javascript">
33313  */
33314
33315 if(Roo.dd.DropZone){
33316     
33317 Roo.tree.TreeDropZone = function(tree, config){
33318     this.allowParentInsert = false;
33319     this.allowContainerDrop = false;
33320     this.appendOnly = false;
33321     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
33322     this.tree = tree;
33323     this.lastInsertClass = "x-tree-no-status";
33324     this.dragOverData = {};
33325 };
33326
33327 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
33328     ddGroup : "TreeDD",
33329     
33330     expandDelay : 1000,
33331     
33332     expandNode : function(node){
33333         if(node.hasChildNodes() && !node.isExpanded()){
33334             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
33335         }
33336     },
33337     
33338     queueExpand : function(node){
33339         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
33340     },
33341     
33342     cancelExpand : function(){
33343         if(this.expandProcId){
33344             clearTimeout(this.expandProcId);
33345             this.expandProcId = false;
33346         }
33347     },
33348     
33349     isValidDropPoint : function(n, pt, dd, e, data){
33350         if(!n || !data){ return false; }
33351         var targetNode = n.node;
33352         var dropNode = data.node;
33353         // default drop rules
33354         if(!(targetNode && targetNode.isTarget && pt)){
33355             return false;
33356         }
33357         if(pt == "append" && targetNode.allowChildren === false){
33358             return false;
33359         }
33360         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
33361             return false;
33362         }
33363         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
33364             return false;
33365         }
33366         // reuse the object
33367         var overEvent = this.dragOverData;
33368         overEvent.tree = this.tree;
33369         overEvent.target = targetNode;
33370         overEvent.data = data;
33371         overEvent.point = pt;
33372         overEvent.source = dd;
33373         overEvent.rawEvent = e;
33374         overEvent.dropNode = dropNode;
33375         overEvent.cancel = false;  
33376         var result = this.tree.fireEvent("nodedragover", overEvent);
33377         return overEvent.cancel === false && result !== false;
33378     },
33379     
33380     getDropPoint : function(e, n, dd){
33381         var tn = n.node;
33382         if(tn.isRoot){
33383             return tn.allowChildren !== false ? "append" : false; // always append for root
33384         }
33385         var dragEl = n.ddel;
33386         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
33387         var y = Roo.lib.Event.getPageY(e);
33388         //var noAppend = tn.allowChildren === false || tn.isLeaf();
33389         
33390         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
33391         var noAppend = tn.allowChildren === false;
33392         if(this.appendOnly || tn.parentNode.allowChildren === false){
33393             return noAppend ? false : "append";
33394         }
33395         var noBelow = false;
33396         if(!this.allowParentInsert){
33397             noBelow = tn.hasChildNodes() && tn.isExpanded();
33398         }
33399         var q = (b - t) / (noAppend ? 2 : 3);
33400         if(y >= t && y < (t + q)){
33401             return "above";
33402         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
33403             return "below";
33404         }else{
33405             return "append";
33406         }
33407     },
33408     
33409     onNodeEnter : function(n, dd, e, data){
33410         this.cancelExpand();
33411     },
33412     
33413     onNodeOver : function(n, dd, e, data){
33414         var pt = this.getDropPoint(e, n, dd);
33415         var node = n.node;
33416         
33417         // auto node expand check
33418         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
33419             this.queueExpand(node);
33420         }else if(pt != "append"){
33421             this.cancelExpand();
33422         }
33423         
33424         // set the insert point style on the target node
33425         var returnCls = this.dropNotAllowed;
33426         if(this.isValidDropPoint(n, pt, dd, e, data)){
33427            if(pt){
33428                var el = n.ddel;
33429                var cls;
33430                if(pt == "above"){
33431                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
33432                    cls = "x-tree-drag-insert-above";
33433                }else if(pt == "below"){
33434                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
33435                    cls = "x-tree-drag-insert-below";
33436                }else{
33437                    returnCls = "x-tree-drop-ok-append";
33438                    cls = "x-tree-drag-append";
33439                }
33440                if(this.lastInsertClass != cls){
33441                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
33442                    this.lastInsertClass = cls;
33443                }
33444            }
33445        }
33446        return returnCls;
33447     },
33448     
33449     onNodeOut : function(n, dd, e, data){
33450         this.cancelExpand();
33451         this.removeDropIndicators(n);
33452     },
33453     
33454     onNodeDrop : function(n, dd, e, data){
33455         var point = this.getDropPoint(e, n, dd);
33456         var targetNode = n.node;
33457         targetNode.ui.startDrop();
33458         if(!this.isValidDropPoint(n, point, dd, e, data)){
33459             targetNode.ui.endDrop();
33460             return false;
33461         }
33462         // first try to find the drop node
33463         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
33464         var dropEvent = {
33465             tree : this.tree,
33466             target: targetNode,
33467             data: data,
33468             point: point,
33469             source: dd,
33470             rawEvent: e,
33471             dropNode: dropNode,
33472             cancel: !dropNode   
33473         };
33474         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
33475         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
33476             targetNode.ui.endDrop();
33477             return false;
33478         }
33479         // allow target changing
33480         targetNode = dropEvent.target;
33481         if(point == "append" && !targetNode.isExpanded()){
33482             targetNode.expand(false, null, function(){
33483                 this.completeDrop(dropEvent);
33484             }.createDelegate(this));
33485         }else{
33486             this.completeDrop(dropEvent);
33487         }
33488         return true;
33489     },
33490     
33491     completeDrop : function(de){
33492         var ns = de.dropNode, p = de.point, t = de.target;
33493         if(!(ns instanceof Array)){
33494             ns = [ns];
33495         }
33496         var n;
33497         for(var i = 0, len = ns.length; i < len; i++){
33498             n = ns[i];
33499             if(p == "above"){
33500                 t.parentNode.insertBefore(n, t);
33501             }else if(p == "below"){
33502                 t.parentNode.insertBefore(n, t.nextSibling);
33503             }else{
33504                 t.appendChild(n);
33505             }
33506         }
33507         n.ui.focus();
33508         if(this.tree.hlDrop){
33509             n.ui.highlight();
33510         }
33511         t.ui.endDrop();
33512         this.tree.fireEvent("nodedrop", de);
33513     },
33514     
33515     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
33516         if(this.tree.hlDrop){
33517             dropNode.ui.focus();
33518             dropNode.ui.highlight();
33519         }
33520         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
33521     },
33522     
33523     getTree : function(){
33524         return this.tree;
33525     },
33526     
33527     removeDropIndicators : function(n){
33528         if(n && n.ddel){
33529             var el = n.ddel;
33530             Roo.fly(el).removeClass([
33531                     "x-tree-drag-insert-above",
33532                     "x-tree-drag-insert-below",
33533                     "x-tree-drag-append"]);
33534             this.lastInsertClass = "_noclass";
33535         }
33536     },
33537     
33538     beforeDragDrop : function(target, e, id){
33539         this.cancelExpand();
33540         return true;
33541     },
33542     
33543     afterRepair : function(data){
33544         if(data && Roo.enableFx){
33545             data.node.ui.highlight();
33546         }
33547         this.hideProxy();
33548     }    
33549 });
33550
33551 }
33552 /*
33553  * Based on:
33554  * Ext JS Library 1.1.1
33555  * Copyright(c) 2006-2007, Ext JS, LLC.
33556  *
33557  * Originally Released Under LGPL - original licence link has changed is not relivant.
33558  *
33559  * Fork - LGPL
33560  * <script type="text/javascript">
33561  */
33562  
33563
33564 if(Roo.dd.DragZone){
33565 Roo.tree.TreeDragZone = function(tree, config){
33566     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
33567     this.tree = tree;
33568 };
33569
33570 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
33571     ddGroup : "TreeDD",
33572     
33573     onBeforeDrag : function(data, e){
33574         var n = data.node;
33575         return n && n.draggable && !n.disabled;
33576     },
33577     
33578     onInitDrag : function(e){
33579         var data = this.dragData;
33580         this.tree.getSelectionModel().select(data.node);
33581         this.proxy.update("");
33582         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
33583         this.tree.fireEvent("startdrag", this.tree, data.node, e);
33584     },
33585     
33586     getRepairXY : function(e, data){
33587         return data.node.ui.getDDRepairXY();
33588     },
33589     
33590     onEndDrag : function(data, e){
33591         this.tree.fireEvent("enddrag", this.tree, data.node, e);
33592     },
33593     
33594     onValidDrop : function(dd, e, id){
33595         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
33596         this.hideProxy();
33597     },
33598     
33599     beforeInvalidDrop : function(e, id){
33600         // this scrolls the original position back into view
33601         var sm = this.tree.getSelectionModel();
33602         sm.clearSelections();
33603         sm.select(this.dragData.node);
33604     }
33605 });
33606 }/*
33607  * Based on:
33608  * Ext JS Library 1.1.1
33609  * Copyright(c) 2006-2007, Ext JS, LLC.
33610  *
33611  * Originally Released Under LGPL - original licence link has changed is not relivant.
33612  *
33613  * Fork - LGPL
33614  * <script type="text/javascript">
33615  */
33616 /**
33617  * @class Roo.tree.TreeEditor
33618  * @extends Roo.Editor
33619  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
33620  * as the editor field.
33621  * @constructor
33622  * @param {Object} config (used to be the tree panel.)
33623  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
33624  * 
33625  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
33626  * @cfg {Roo.form.TextField|Object} field The field configuration
33627  *
33628  * 
33629  */
33630 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
33631     var tree = config;
33632     var field;
33633     if (oldconfig) { // old style..
33634         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
33635     } else {
33636         // new style..
33637         tree = config.tree;
33638         config.field = config.field  || {};
33639         config.field.xtype = 'TextField';
33640         field = Roo.factory(config.field, Roo.form);
33641     }
33642     config = config || {};
33643     
33644     
33645     this.addEvents({
33646         /**
33647          * @event beforenodeedit
33648          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
33649          * false from the handler of this event.
33650          * @param {Editor} this
33651          * @param {Roo.tree.Node} node 
33652          */
33653         "beforenodeedit" : true
33654     });
33655     
33656     //Roo.log(config);
33657     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
33658
33659     this.tree = tree;
33660
33661     tree.on('beforeclick', this.beforeNodeClick, this);
33662     tree.getTreeEl().on('mousedown', this.hide, this);
33663     this.on('complete', this.updateNode, this);
33664     this.on('beforestartedit', this.fitToTree, this);
33665     this.on('startedit', this.bindScroll, this, {delay:10});
33666     this.on('specialkey', this.onSpecialKey, this);
33667 };
33668
33669 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
33670     /**
33671      * @cfg {String} alignment
33672      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
33673      */
33674     alignment: "l-l",
33675     // inherit
33676     autoSize: false,
33677     /**
33678      * @cfg {Boolean} hideEl
33679      * True to hide the bound element while the editor is displayed (defaults to false)
33680      */
33681     hideEl : false,
33682     /**
33683      * @cfg {String} cls
33684      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
33685      */
33686     cls: "x-small-editor x-tree-editor",
33687     /**
33688      * @cfg {Boolean} shim
33689      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
33690      */
33691     shim:false,
33692     // inherit
33693     shadow:"frame",
33694     /**
33695      * @cfg {Number} maxWidth
33696      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
33697      * the containing tree element's size, it will be automatically limited for you to the container width, taking
33698      * scroll and client offsets into account prior to each edit.
33699      */
33700     maxWidth: 250,
33701
33702     editDelay : 350,
33703
33704     // private
33705     fitToTree : function(ed, el){
33706         var td = this.tree.getTreeEl().dom, nd = el.dom;
33707         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
33708             td.scrollLeft = nd.offsetLeft;
33709         }
33710         var w = Math.min(
33711                 this.maxWidth,
33712                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
33713         this.setSize(w, '');
33714         
33715         return this.fireEvent('beforenodeedit', this, this.editNode);
33716         
33717     },
33718
33719     // private
33720     triggerEdit : function(node){
33721         this.completeEdit();
33722         this.editNode = node;
33723         this.startEdit(node.ui.textNode, node.text);
33724     },
33725
33726     // private
33727     bindScroll : function(){
33728         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
33729     },
33730
33731     // private
33732     beforeNodeClick : function(node, e){
33733         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
33734         this.lastClick = new Date();
33735         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
33736             e.stopEvent();
33737             this.triggerEdit(node);
33738             return false;
33739         }
33740         return true;
33741     },
33742
33743     // private
33744     updateNode : function(ed, value){
33745         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
33746         this.editNode.setText(value);
33747     },
33748
33749     // private
33750     onHide : function(){
33751         Roo.tree.TreeEditor.superclass.onHide.call(this);
33752         if(this.editNode){
33753             this.editNode.ui.focus();
33754         }
33755     },
33756
33757     // private
33758     onSpecialKey : function(field, e){
33759         var k = e.getKey();
33760         if(k == e.ESC){
33761             e.stopEvent();
33762             this.cancelEdit();
33763         }else if(k == e.ENTER && !e.hasModifier()){
33764             e.stopEvent();
33765             this.completeEdit();
33766         }
33767     }
33768 });//<Script type="text/javascript">
33769 /*
33770  * Based on:
33771  * Ext JS Library 1.1.1
33772  * Copyright(c) 2006-2007, Ext JS, LLC.
33773  *
33774  * Originally Released Under LGPL - original licence link has changed is not relivant.
33775  *
33776  * Fork - LGPL
33777  * <script type="text/javascript">
33778  */
33779  
33780 /**
33781  * Not documented??? - probably should be...
33782  */
33783
33784 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
33785     //focus: Roo.emptyFn, // prevent odd scrolling behavior
33786     
33787     renderElements : function(n, a, targetNode, bulkRender){
33788         //consel.log("renderElements?");
33789         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
33790
33791         var t = n.getOwnerTree();
33792         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
33793         
33794         var cols = t.columns;
33795         var bw = t.borderWidth;
33796         var c = cols[0];
33797         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
33798          var cb = typeof a.checked == "boolean";
33799         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
33800         var colcls = 'x-t-' + tid + '-c0';
33801         var buf = [
33802             '<li class="x-tree-node">',
33803             
33804                 
33805                 '<div class="x-tree-node-el ', a.cls,'">',
33806                     // extran...
33807                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
33808                 
33809                 
33810                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
33811                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
33812                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
33813                            (a.icon ? ' x-tree-node-inline-icon' : ''),
33814                            (a.iconCls ? ' '+a.iconCls : ''),
33815                            '" unselectable="on" />',
33816                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
33817                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
33818                              
33819                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
33820                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
33821                             '<span unselectable="on" qtip="' + tx + '">',
33822                              tx,
33823                              '</span></a>' ,
33824                     '</div>',
33825                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
33826                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
33827                  ];
33828         for(var i = 1, len = cols.length; i < len; i++){
33829             c = cols[i];
33830             colcls = 'x-t-' + tid + '-c' +i;
33831             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
33832             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
33833                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
33834                       "</div>");
33835          }
33836          
33837          buf.push(
33838             '</a>',
33839             '<div class="x-clear"></div></div>',
33840             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
33841             "</li>");
33842         
33843         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
33844             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
33845                                 n.nextSibling.ui.getEl(), buf.join(""));
33846         }else{
33847             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
33848         }
33849         var el = this.wrap.firstChild;
33850         this.elRow = el;
33851         this.elNode = el.firstChild;
33852         this.ranchor = el.childNodes[1];
33853         this.ctNode = this.wrap.childNodes[1];
33854         var cs = el.firstChild.childNodes;
33855         this.indentNode = cs[0];
33856         this.ecNode = cs[1];
33857         this.iconNode = cs[2];
33858         var index = 3;
33859         if(cb){
33860             this.checkbox = cs[3];
33861             index++;
33862         }
33863         this.anchor = cs[index];
33864         
33865         this.textNode = cs[index].firstChild;
33866         
33867         //el.on("click", this.onClick, this);
33868         //el.on("dblclick", this.onDblClick, this);
33869         
33870         
33871        // console.log(this);
33872     },
33873     initEvents : function(){
33874         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
33875         
33876             
33877         var a = this.ranchor;
33878
33879         var el = Roo.get(a);
33880
33881         if(Roo.isOpera){ // opera render bug ignores the CSS
33882             el.setStyle("text-decoration", "none");
33883         }
33884
33885         el.on("click", this.onClick, this);
33886         el.on("dblclick", this.onDblClick, this);
33887         el.on("contextmenu", this.onContextMenu, this);
33888         
33889     },
33890     
33891     /*onSelectedChange : function(state){
33892         if(state){
33893             this.focus();
33894             this.addClass("x-tree-selected");
33895         }else{
33896             //this.blur();
33897             this.removeClass("x-tree-selected");
33898         }
33899     },*/
33900     addClass : function(cls){
33901         if(this.elRow){
33902             Roo.fly(this.elRow).addClass(cls);
33903         }
33904         
33905     },
33906     
33907     
33908     removeClass : function(cls){
33909         if(this.elRow){
33910             Roo.fly(this.elRow).removeClass(cls);
33911         }
33912     }
33913
33914     
33915     
33916 });//<Script type="text/javascript">
33917
33918 /*
33919  * Based on:
33920  * Ext JS Library 1.1.1
33921  * Copyright(c) 2006-2007, Ext JS, LLC.
33922  *
33923  * Originally Released Under LGPL - original licence link has changed is not relivant.
33924  *
33925  * Fork - LGPL
33926  * <script type="text/javascript">
33927  */
33928  
33929
33930 /**
33931  * @class Roo.tree.ColumnTree
33932  * @extends Roo.data.TreePanel
33933  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
33934  * @cfg {int} borderWidth  compined right/left border allowance
33935  * @constructor
33936  * @param {String/HTMLElement/Element} el The container element
33937  * @param {Object} config
33938  */
33939 Roo.tree.ColumnTree =  function(el, config)
33940 {
33941    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
33942    this.addEvents({
33943         /**
33944         * @event resize
33945         * Fire this event on a container when it resizes
33946         * @param {int} w Width
33947         * @param {int} h Height
33948         */
33949        "resize" : true
33950     });
33951     this.on('resize', this.onResize, this);
33952 };
33953
33954 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
33955     //lines:false,
33956     
33957     
33958     borderWidth: Roo.isBorderBox ? 0 : 2, 
33959     headEls : false,
33960     
33961     render : function(){
33962         // add the header.....
33963        
33964         Roo.tree.ColumnTree.superclass.render.apply(this);
33965         
33966         this.el.addClass('x-column-tree');
33967         
33968         this.headers = this.el.createChild(
33969             {cls:'x-tree-headers'},this.innerCt.dom);
33970    
33971         var cols = this.columns, c;
33972         var totalWidth = 0;
33973         this.headEls = [];
33974         var  len = cols.length;
33975         for(var i = 0; i < len; i++){
33976              c = cols[i];
33977              totalWidth += c.width;
33978             this.headEls.push(this.headers.createChild({
33979                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
33980                  cn: {
33981                      cls:'x-tree-hd-text',
33982                      html: c.header
33983                  },
33984                  style:'width:'+(c.width-this.borderWidth)+'px;'
33985              }));
33986         }
33987         this.headers.createChild({cls:'x-clear'});
33988         // prevent floats from wrapping when clipped
33989         this.headers.setWidth(totalWidth);
33990         //this.innerCt.setWidth(totalWidth);
33991         this.innerCt.setStyle({ overflow: 'auto' });
33992         this.onResize(this.width, this.height);
33993              
33994         
33995     },
33996     onResize : function(w,h)
33997     {
33998         this.height = h;
33999         this.width = w;
34000         // resize cols..
34001         this.innerCt.setWidth(this.width);
34002         this.innerCt.setHeight(this.height-20);
34003         
34004         // headers...
34005         var cols = this.columns, c;
34006         var totalWidth = 0;
34007         var expEl = false;
34008         var len = cols.length;
34009         for(var i = 0; i < len; i++){
34010             c = cols[i];
34011             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
34012                 // it's the expander..
34013                 expEl  = this.headEls[i];
34014                 continue;
34015             }
34016             totalWidth += c.width;
34017             
34018         }
34019         if (expEl) {
34020             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
34021         }
34022         this.headers.setWidth(w-20);
34023
34024         
34025         
34026         
34027     }
34028 });
34029 /*
34030  * Based on:
34031  * Ext JS Library 1.1.1
34032  * Copyright(c) 2006-2007, Ext JS, LLC.
34033  *
34034  * Originally Released Under LGPL - original licence link has changed is not relivant.
34035  *
34036  * Fork - LGPL
34037  * <script type="text/javascript">
34038  */
34039  
34040 /**
34041  * @class Roo.menu.Menu
34042  * @extends Roo.util.Observable
34043  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
34044  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
34045  * @constructor
34046  * Creates a new Menu
34047  * @param {Object} config Configuration options
34048  */
34049 Roo.menu.Menu = function(config){
34050     Roo.apply(this, config);
34051     this.id = this.id || Roo.id();
34052     this.addEvents({
34053         /**
34054          * @event beforeshow
34055          * Fires before this menu is displayed
34056          * @param {Roo.menu.Menu} this
34057          */
34058         beforeshow : true,
34059         /**
34060          * @event beforehide
34061          * Fires before this menu is hidden
34062          * @param {Roo.menu.Menu} this
34063          */
34064         beforehide : true,
34065         /**
34066          * @event show
34067          * Fires after this menu is displayed
34068          * @param {Roo.menu.Menu} this
34069          */
34070         show : true,
34071         /**
34072          * @event hide
34073          * Fires after this menu is hidden
34074          * @param {Roo.menu.Menu} this
34075          */
34076         hide : true,
34077         /**
34078          * @event click
34079          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
34080          * @param {Roo.menu.Menu} this
34081          * @param {Roo.menu.Item} menuItem The menu item that was clicked
34082          * @param {Roo.EventObject} e
34083          */
34084         click : true,
34085         /**
34086          * @event mouseover
34087          * Fires when the mouse is hovering over this menu
34088          * @param {Roo.menu.Menu} this
34089          * @param {Roo.EventObject} e
34090          * @param {Roo.menu.Item} menuItem The menu item that was clicked
34091          */
34092         mouseover : true,
34093         /**
34094          * @event mouseout
34095          * Fires when the mouse exits this menu
34096          * @param {Roo.menu.Menu} this
34097          * @param {Roo.EventObject} e
34098          * @param {Roo.menu.Item} menuItem The menu item that was clicked
34099          */
34100         mouseout : true,
34101         /**
34102          * @event itemclick
34103          * Fires when a menu item contained in this menu is clicked
34104          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
34105          * @param {Roo.EventObject} e
34106          */
34107         itemclick: true
34108     });
34109     if (this.registerMenu) {
34110         Roo.menu.MenuMgr.register(this);
34111     }
34112     
34113     var mis = this.items;
34114     this.items = new Roo.util.MixedCollection();
34115     if(mis){
34116         this.add.apply(this, mis);
34117     }
34118 };
34119
34120 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
34121     /**
34122      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
34123      */
34124     minWidth : 120,
34125     /**
34126      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
34127      * for bottom-right shadow (defaults to "sides")
34128      */
34129     shadow : "sides",
34130     /**
34131      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
34132      * this menu (defaults to "tl-tr?")
34133      */
34134     subMenuAlign : "tl-tr?",
34135     /**
34136      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
34137      * relative to its element of origin (defaults to "tl-bl?")
34138      */
34139     defaultAlign : "tl-bl?",
34140     /**
34141      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
34142      */
34143     allowOtherMenus : false,
34144     /**
34145      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
34146      */
34147     registerMenu : true,
34148
34149     hidden:true,
34150
34151     // private
34152     render : function(){
34153         if(this.el){
34154             return;
34155         }
34156         var el = this.el = new Roo.Layer({
34157             cls: "x-menu",
34158             shadow:this.shadow,
34159             constrain: false,
34160             parentEl: this.parentEl || document.body,
34161             zindex:15000
34162         });
34163
34164         this.keyNav = new Roo.menu.MenuNav(this);
34165
34166         if(this.plain){
34167             el.addClass("x-menu-plain");
34168         }
34169         if(this.cls){
34170             el.addClass(this.cls);
34171         }
34172         // generic focus element
34173         this.focusEl = el.createChild({
34174             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
34175         });
34176         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
34177         ul.on("click", this.onClick, this);
34178         ul.on("mouseover", this.onMouseOver, this);
34179         ul.on("mouseout", this.onMouseOut, this);
34180         this.items.each(function(item){
34181             var li = document.createElement("li");
34182             li.className = "x-menu-list-item";
34183             ul.dom.appendChild(li);
34184             item.render(li, this);
34185         }, this);
34186         this.ul = ul;
34187         this.autoWidth();
34188     },
34189
34190     // private
34191     autoWidth : function(){
34192         var el = this.el, ul = this.ul;
34193         if(!el){
34194             return;
34195         }
34196         var w = this.width;
34197         if(w){
34198             el.setWidth(w);
34199         }else if(Roo.isIE){
34200             el.setWidth(this.minWidth);
34201             var t = el.dom.offsetWidth; // force recalc
34202             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
34203         }
34204     },
34205
34206     // private
34207     delayAutoWidth : function(){
34208         if(this.rendered){
34209             if(!this.awTask){
34210                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
34211             }
34212             this.awTask.delay(20);
34213         }
34214     },
34215
34216     // private
34217     findTargetItem : function(e){
34218         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
34219         if(t && t.menuItemId){
34220             return this.items.get(t.menuItemId);
34221         }
34222     },
34223
34224     // private
34225     onClick : function(e){
34226         var t;
34227         if(t = this.findTargetItem(e)){
34228             t.onClick(e);
34229             this.fireEvent("click", this, t, e);
34230         }
34231     },
34232
34233     // private
34234     setActiveItem : function(item, autoExpand){
34235         if(item != this.activeItem){
34236             if(this.activeItem){
34237                 this.activeItem.deactivate();
34238             }
34239             this.activeItem = item;
34240             item.activate(autoExpand);
34241         }else if(autoExpand){
34242             item.expandMenu();
34243         }
34244     },
34245
34246     // private
34247     tryActivate : function(start, step){
34248         var items = this.items;
34249         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
34250             var item = items.get(i);
34251             if(!item.disabled && item.canActivate){
34252                 this.setActiveItem(item, false);
34253                 return item;
34254             }
34255         }
34256         return false;
34257     },
34258
34259     // private
34260     onMouseOver : function(e){
34261         var t;
34262         if(t = this.findTargetItem(e)){
34263             if(t.canActivate && !t.disabled){
34264                 this.setActiveItem(t, true);
34265             }
34266         }
34267         this.fireEvent("mouseover", this, e, t);
34268     },
34269
34270     // private
34271     onMouseOut : function(e){
34272         var t;
34273         if(t = this.findTargetItem(e)){
34274             if(t == this.activeItem && t.shouldDeactivate(e)){
34275                 this.activeItem.deactivate();
34276                 delete this.activeItem;
34277             }
34278         }
34279         this.fireEvent("mouseout", this, e, t);
34280     },
34281
34282     /**
34283      * Read-only.  Returns true if the menu is currently displayed, else false.
34284      * @type Boolean
34285      */
34286     isVisible : function(){
34287         return this.el && !this.hidden;
34288     },
34289
34290     /**
34291      * Displays this menu relative to another element
34292      * @param {String/HTMLElement/Roo.Element} element The element to align to
34293      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
34294      * the element (defaults to this.defaultAlign)
34295      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
34296      */
34297     show : function(el, pos, parentMenu){
34298         this.parentMenu = parentMenu;
34299         if(!this.el){
34300             this.render();
34301         }
34302         this.fireEvent("beforeshow", this);
34303         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
34304     },
34305
34306     /**
34307      * Displays this menu at a specific xy position
34308      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
34309      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
34310      */
34311     showAt : function(xy, parentMenu, /* private: */_e){
34312         this.parentMenu = parentMenu;
34313         if(!this.el){
34314             this.render();
34315         }
34316         if(_e !== false){
34317             this.fireEvent("beforeshow", this);
34318             xy = this.el.adjustForConstraints(xy);
34319         }
34320         this.el.setXY(xy);
34321         this.el.show();
34322         this.hidden = false;
34323         this.focus();
34324         this.fireEvent("show", this);
34325     },
34326
34327     focus : function(){
34328         if(!this.hidden){
34329             this.doFocus.defer(50, this);
34330         }
34331     },
34332
34333     doFocus : function(){
34334         if(!this.hidden){
34335             this.focusEl.focus();
34336         }
34337     },
34338
34339     /**
34340      * Hides this menu and optionally all parent menus
34341      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
34342      */
34343     hide : function(deep){
34344         if(this.el && this.isVisible()){
34345             this.fireEvent("beforehide", this);
34346             if(this.activeItem){
34347                 this.activeItem.deactivate();
34348                 this.activeItem = null;
34349             }
34350             this.el.hide();
34351             this.hidden = true;
34352             this.fireEvent("hide", this);
34353         }
34354         if(deep === true && this.parentMenu){
34355             this.parentMenu.hide(true);
34356         }
34357     },
34358
34359     /**
34360      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
34361      * Any of the following are valid:
34362      * <ul>
34363      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
34364      * <li>An HTMLElement object which will be converted to a menu item</li>
34365      * <li>A menu item config object that will be created as a new menu item</li>
34366      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
34367      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
34368      * </ul>
34369      * Usage:
34370      * <pre><code>
34371 // Create the menu
34372 var menu = new Roo.menu.Menu();
34373
34374 // Create a menu item to add by reference
34375 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
34376
34377 // Add a bunch of items at once using different methods.
34378 // Only the last item added will be returned.
34379 var item = menu.add(
34380     menuItem,                // add existing item by ref
34381     'Dynamic Item',          // new TextItem
34382     '-',                     // new separator
34383     { text: 'Config Item' }  // new item by config
34384 );
34385 </code></pre>
34386      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
34387      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
34388      */
34389     add : function(){
34390         var a = arguments, l = a.length, item;
34391         for(var i = 0; i < l; i++){
34392             var el = a[i];
34393             if ((typeof(el) == "object") && el.xtype && el.xns) {
34394                 el = Roo.factory(el, Roo.menu);
34395             }
34396             
34397             if(el.render){ // some kind of Item
34398                 item = this.addItem(el);
34399             }else if(typeof el == "string"){ // string
34400                 if(el == "separator" || el == "-"){
34401                     item = this.addSeparator();
34402                 }else{
34403                     item = this.addText(el);
34404                 }
34405             }else if(el.tagName || el.el){ // element
34406                 item = this.addElement(el);
34407             }else if(typeof el == "object"){ // must be menu item config?
34408                 item = this.addMenuItem(el);
34409             }
34410         }
34411         return item;
34412     },
34413
34414     /**
34415      * Returns this menu's underlying {@link Roo.Element} object
34416      * @return {Roo.Element} The element
34417      */
34418     getEl : function(){
34419         if(!this.el){
34420             this.render();
34421         }
34422         return this.el;
34423     },
34424
34425     /**
34426      * Adds a separator bar to the menu
34427      * @return {Roo.menu.Item} The menu item that was added
34428      */
34429     addSeparator : function(){
34430         return this.addItem(new Roo.menu.Separator());
34431     },
34432
34433     /**
34434      * Adds an {@link Roo.Element} object to the menu
34435      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
34436      * @return {Roo.menu.Item} The menu item that was added
34437      */
34438     addElement : function(el){
34439         return this.addItem(new Roo.menu.BaseItem(el));
34440     },
34441
34442     /**
34443      * Adds an existing object based on {@link Roo.menu.Item} to the menu
34444      * @param {Roo.menu.Item} item The menu item to add
34445      * @return {Roo.menu.Item} The menu item that was added
34446      */
34447     addItem : function(item){
34448         this.items.add(item);
34449         if(this.ul){
34450             var li = document.createElement("li");
34451             li.className = "x-menu-list-item";
34452             this.ul.dom.appendChild(li);
34453             item.render(li, this);
34454             this.delayAutoWidth();
34455         }
34456         return item;
34457     },
34458
34459     /**
34460      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
34461      * @param {Object} config A MenuItem config object
34462      * @return {Roo.menu.Item} The menu item that was added
34463      */
34464     addMenuItem : function(config){
34465         if(!(config instanceof Roo.menu.Item)){
34466             if(typeof config.checked == "boolean"){ // must be check menu item config?
34467                 config = new Roo.menu.CheckItem(config);
34468             }else{
34469                 config = new Roo.menu.Item(config);
34470             }
34471         }
34472         return this.addItem(config);
34473     },
34474
34475     /**
34476      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
34477      * @param {String} text The text to display in the menu item
34478      * @return {Roo.menu.Item} The menu item that was added
34479      */
34480     addText : function(text){
34481         return this.addItem(new Roo.menu.TextItem({ text : text }));
34482     },
34483
34484     /**
34485      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
34486      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
34487      * @param {Roo.menu.Item} item The menu item to add
34488      * @return {Roo.menu.Item} The menu item that was added
34489      */
34490     insert : function(index, item){
34491         this.items.insert(index, item);
34492         if(this.ul){
34493             var li = document.createElement("li");
34494             li.className = "x-menu-list-item";
34495             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
34496             item.render(li, this);
34497             this.delayAutoWidth();
34498         }
34499         return item;
34500     },
34501
34502     /**
34503      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
34504      * @param {Roo.menu.Item} item The menu item to remove
34505      */
34506     remove : function(item){
34507         this.items.removeKey(item.id);
34508         item.destroy();
34509     },
34510
34511     /**
34512      * Removes and destroys all items in the menu
34513      */
34514     removeAll : function(){
34515         var f;
34516         while(f = this.items.first()){
34517             this.remove(f);
34518         }
34519     }
34520 });
34521
34522 // MenuNav is a private utility class used internally by the Menu
34523 Roo.menu.MenuNav = function(menu){
34524     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
34525     this.scope = this.menu = menu;
34526 };
34527
34528 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
34529     doRelay : function(e, h){
34530         var k = e.getKey();
34531         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
34532             this.menu.tryActivate(0, 1);
34533             return false;
34534         }
34535         return h.call(this.scope || this, e, this.menu);
34536     },
34537
34538     up : function(e, m){
34539         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
34540             m.tryActivate(m.items.length-1, -1);
34541         }
34542     },
34543
34544     down : function(e, m){
34545         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
34546             m.tryActivate(0, 1);
34547         }
34548     },
34549
34550     right : function(e, m){
34551         if(m.activeItem){
34552             m.activeItem.expandMenu(true);
34553         }
34554     },
34555
34556     left : function(e, m){
34557         m.hide();
34558         if(m.parentMenu && m.parentMenu.activeItem){
34559             m.parentMenu.activeItem.activate();
34560         }
34561     },
34562
34563     enter : function(e, m){
34564         if(m.activeItem){
34565             e.stopPropagation();
34566             m.activeItem.onClick(e);
34567             m.fireEvent("click", this, m.activeItem);
34568             return true;
34569         }
34570     }
34571 });/*
34572  * Based on:
34573  * Ext JS Library 1.1.1
34574  * Copyright(c) 2006-2007, Ext JS, LLC.
34575  *
34576  * Originally Released Under LGPL - original licence link has changed is not relivant.
34577  *
34578  * Fork - LGPL
34579  * <script type="text/javascript">
34580  */
34581  
34582 /**
34583  * @class Roo.menu.MenuMgr
34584  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
34585  * @singleton
34586  */
34587 Roo.menu.MenuMgr = function(){
34588    var menus, active, groups = {}, attached = false, lastShow = new Date();
34589
34590    // private - called when first menu is created
34591    function init(){
34592        menus = {};
34593        active = new Roo.util.MixedCollection();
34594        Roo.get(document).addKeyListener(27, function(){
34595            if(active.length > 0){
34596                hideAll();
34597            }
34598        });
34599    }
34600
34601    // private
34602    function hideAll(){
34603        if(active && active.length > 0){
34604            var c = active.clone();
34605            c.each(function(m){
34606                m.hide();
34607            });
34608        }
34609    }
34610
34611    // private
34612    function onHide(m){
34613        active.remove(m);
34614        if(active.length < 1){
34615            Roo.get(document).un("mousedown", onMouseDown);
34616            attached = false;
34617        }
34618    }
34619
34620    // private
34621    function onShow(m){
34622        var last = active.last();
34623        lastShow = new Date();
34624        active.add(m);
34625        if(!attached){
34626            Roo.get(document).on("mousedown", onMouseDown);
34627            attached = true;
34628        }
34629        if(m.parentMenu){
34630           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
34631           m.parentMenu.activeChild = m;
34632        }else if(last && last.isVisible()){
34633           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
34634        }
34635    }
34636
34637    // private
34638    function onBeforeHide(m){
34639        if(m.activeChild){
34640            m.activeChild.hide();
34641        }
34642        if(m.autoHideTimer){
34643            clearTimeout(m.autoHideTimer);
34644            delete m.autoHideTimer;
34645        }
34646    }
34647
34648    // private
34649    function onBeforeShow(m){
34650        var pm = m.parentMenu;
34651        if(!pm && !m.allowOtherMenus){
34652            hideAll();
34653        }else if(pm && pm.activeChild && active != m){
34654            pm.activeChild.hide();
34655        }
34656    }
34657
34658    // private
34659    function onMouseDown(e){
34660        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
34661            hideAll();
34662        }
34663    }
34664
34665    // private
34666    function onBeforeCheck(mi, state){
34667        if(state){
34668            var g = groups[mi.group];
34669            for(var i = 0, l = g.length; i < l; i++){
34670                if(g[i] != mi){
34671                    g[i].setChecked(false);
34672                }
34673            }
34674        }
34675    }
34676
34677    return {
34678
34679        /**
34680         * Hides all menus that are currently visible
34681         */
34682        hideAll : function(){
34683             hideAll();  
34684        },
34685
34686        // private
34687        register : function(menu){
34688            if(!menus){
34689                init();
34690            }
34691            menus[menu.id] = menu;
34692            menu.on("beforehide", onBeforeHide);
34693            menu.on("hide", onHide);
34694            menu.on("beforeshow", onBeforeShow);
34695            menu.on("show", onShow);
34696            var g = menu.group;
34697            if(g && menu.events["checkchange"]){
34698                if(!groups[g]){
34699                    groups[g] = [];
34700                }
34701                groups[g].push(menu);
34702                menu.on("checkchange", onCheck);
34703            }
34704        },
34705
34706         /**
34707          * Returns a {@link Roo.menu.Menu} object
34708          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
34709          * be used to generate and return a new Menu instance.
34710          */
34711        get : function(menu){
34712            if(typeof menu == "string"){ // menu id
34713                return menus[menu];
34714            }else if(menu.events){  // menu instance
34715                return menu;
34716            }else if(typeof menu.length == 'number'){ // array of menu items?
34717                return new Roo.menu.Menu({items:menu});
34718            }else{ // otherwise, must be a config
34719                return new Roo.menu.Menu(menu);
34720            }
34721        },
34722
34723        // private
34724        unregister : function(menu){
34725            delete menus[menu.id];
34726            menu.un("beforehide", onBeforeHide);
34727            menu.un("hide", onHide);
34728            menu.un("beforeshow", onBeforeShow);
34729            menu.un("show", onShow);
34730            var g = menu.group;
34731            if(g && menu.events["checkchange"]){
34732                groups[g].remove(menu);
34733                menu.un("checkchange", onCheck);
34734            }
34735        },
34736
34737        // private
34738        registerCheckable : function(menuItem){
34739            var g = menuItem.group;
34740            if(g){
34741                if(!groups[g]){
34742                    groups[g] = [];
34743                }
34744                groups[g].push(menuItem);
34745                menuItem.on("beforecheckchange", onBeforeCheck);
34746            }
34747        },
34748
34749        // private
34750        unregisterCheckable : function(menuItem){
34751            var g = menuItem.group;
34752            if(g){
34753                groups[g].remove(menuItem);
34754                menuItem.un("beforecheckchange", onBeforeCheck);
34755            }
34756        }
34757    };
34758 }();/*
34759  * Based on:
34760  * Ext JS Library 1.1.1
34761  * Copyright(c) 2006-2007, Ext JS, LLC.
34762  *
34763  * Originally Released Under LGPL - original licence link has changed is not relivant.
34764  *
34765  * Fork - LGPL
34766  * <script type="text/javascript">
34767  */
34768  
34769
34770 /**
34771  * @class Roo.menu.BaseItem
34772  * @extends Roo.Component
34773  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
34774  * management and base configuration options shared by all menu components.
34775  * @constructor
34776  * Creates a new BaseItem
34777  * @param {Object} config Configuration options
34778  */
34779 Roo.menu.BaseItem = function(config){
34780     Roo.menu.BaseItem.superclass.constructor.call(this, config);
34781
34782     this.addEvents({
34783         /**
34784          * @event click
34785          * Fires when this item is clicked
34786          * @param {Roo.menu.BaseItem} this
34787          * @param {Roo.EventObject} e
34788          */
34789         click: true,
34790         /**
34791          * @event activate
34792          * Fires when this item is activated
34793          * @param {Roo.menu.BaseItem} this
34794          */
34795         activate : true,
34796         /**
34797          * @event deactivate
34798          * Fires when this item is deactivated
34799          * @param {Roo.menu.BaseItem} this
34800          */
34801         deactivate : true
34802     });
34803
34804     if(this.handler){
34805         this.on("click", this.handler, this.scope, true);
34806     }
34807 };
34808
34809 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
34810     /**
34811      * @cfg {Function} handler
34812      * A function that will handle the click event of this menu item (defaults to undefined)
34813      */
34814     /**
34815      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
34816      */
34817     canActivate : false,
34818     /**
34819      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
34820      */
34821     activeClass : "x-menu-item-active",
34822     /**
34823      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
34824      */
34825     hideOnClick : true,
34826     /**
34827      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
34828      */
34829     hideDelay : 100,
34830
34831     // private
34832     ctype: "Roo.menu.BaseItem",
34833
34834     // private
34835     actionMode : "container",
34836
34837     // private
34838     render : function(container, parentMenu){
34839         this.parentMenu = parentMenu;
34840         Roo.menu.BaseItem.superclass.render.call(this, container);
34841         this.container.menuItemId = this.id;
34842     },
34843
34844     // private
34845     onRender : function(container, position){
34846         this.el = Roo.get(this.el);
34847         container.dom.appendChild(this.el.dom);
34848     },
34849
34850     // private
34851     onClick : function(e){
34852         if(!this.disabled && this.fireEvent("click", this, e) !== false
34853                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
34854             this.handleClick(e);
34855         }else{
34856             e.stopEvent();
34857         }
34858     },
34859
34860     // private
34861     activate : function(){
34862         if(this.disabled){
34863             return false;
34864         }
34865         var li = this.container;
34866         li.addClass(this.activeClass);
34867         this.region = li.getRegion().adjust(2, 2, -2, -2);
34868         this.fireEvent("activate", this);
34869         return true;
34870     },
34871
34872     // private
34873     deactivate : function(){
34874         this.container.removeClass(this.activeClass);
34875         this.fireEvent("deactivate", this);
34876     },
34877
34878     // private
34879     shouldDeactivate : function(e){
34880         return !this.region || !this.region.contains(e.getPoint());
34881     },
34882
34883     // private
34884     handleClick : function(e){
34885         if(this.hideOnClick){
34886             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
34887         }
34888     },
34889
34890     // private
34891     expandMenu : function(autoActivate){
34892         // do nothing
34893     },
34894
34895     // private
34896     hideMenu : function(){
34897         // do nothing
34898     }
34899 });/*
34900  * Based on:
34901  * Ext JS Library 1.1.1
34902  * Copyright(c) 2006-2007, Ext JS, LLC.
34903  *
34904  * Originally Released Under LGPL - original licence link has changed is not relivant.
34905  *
34906  * Fork - LGPL
34907  * <script type="text/javascript">
34908  */
34909  
34910 /**
34911  * @class Roo.menu.Adapter
34912  * @extends Roo.menu.BaseItem
34913  * 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.
34914  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
34915  * @constructor
34916  * Creates a new Adapter
34917  * @param {Object} config Configuration options
34918  */
34919 Roo.menu.Adapter = function(component, config){
34920     Roo.menu.Adapter.superclass.constructor.call(this, config);
34921     this.component = component;
34922 };
34923 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
34924     // private
34925     canActivate : true,
34926
34927     // private
34928     onRender : function(container, position){
34929         this.component.render(container);
34930         this.el = this.component.getEl();
34931     },
34932
34933     // private
34934     activate : function(){
34935         if(this.disabled){
34936             return false;
34937         }
34938         this.component.focus();
34939         this.fireEvent("activate", this);
34940         return true;
34941     },
34942
34943     // private
34944     deactivate : function(){
34945         this.fireEvent("deactivate", this);
34946     },
34947
34948     // private
34949     disable : function(){
34950         this.component.disable();
34951         Roo.menu.Adapter.superclass.disable.call(this);
34952     },
34953
34954     // private
34955     enable : function(){
34956         this.component.enable();
34957         Roo.menu.Adapter.superclass.enable.call(this);
34958     }
34959 });/*
34960  * Based on:
34961  * Ext JS Library 1.1.1
34962  * Copyright(c) 2006-2007, Ext JS, LLC.
34963  *
34964  * Originally Released Under LGPL - original licence link has changed is not relivant.
34965  *
34966  * Fork - LGPL
34967  * <script type="text/javascript">
34968  */
34969
34970 /**
34971  * @class Roo.menu.TextItem
34972  * @extends Roo.menu.BaseItem
34973  * Adds a static text string to a menu, usually used as either a heading or group separator.
34974  * Note: old style constructor with text is still supported.
34975  * 
34976  * @constructor
34977  * Creates a new TextItem
34978  * @param {Object} cfg Configuration
34979  */
34980 Roo.menu.TextItem = function(cfg){
34981     if (typeof(cfg) == 'string') {
34982         this.text = cfg;
34983     } else {
34984         Roo.apply(this,cfg);
34985     }
34986     
34987     Roo.menu.TextItem.superclass.constructor.call(this);
34988 };
34989
34990 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
34991     /**
34992      * @cfg {Boolean} text Text to show on item.
34993      */
34994     text : '',
34995     
34996     /**
34997      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
34998      */
34999     hideOnClick : false,
35000     /**
35001      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
35002      */
35003     itemCls : "x-menu-text",
35004
35005     // private
35006     onRender : function(){
35007         var s = document.createElement("span");
35008         s.className = this.itemCls;
35009         s.innerHTML = this.text;
35010         this.el = s;
35011         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
35012     }
35013 });/*
35014  * Based on:
35015  * Ext JS Library 1.1.1
35016  * Copyright(c) 2006-2007, Ext JS, LLC.
35017  *
35018  * Originally Released Under LGPL - original licence link has changed is not relivant.
35019  *
35020  * Fork - LGPL
35021  * <script type="text/javascript">
35022  */
35023
35024 /**
35025  * @class Roo.menu.Separator
35026  * @extends Roo.menu.BaseItem
35027  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
35028  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
35029  * @constructor
35030  * @param {Object} config Configuration options
35031  */
35032 Roo.menu.Separator = function(config){
35033     Roo.menu.Separator.superclass.constructor.call(this, config);
35034 };
35035
35036 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
35037     /**
35038      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
35039      */
35040     itemCls : "x-menu-sep",
35041     /**
35042      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
35043      */
35044     hideOnClick : false,
35045
35046     // private
35047     onRender : function(li){
35048         var s = document.createElement("span");
35049         s.className = this.itemCls;
35050         s.innerHTML = "&#160;";
35051         this.el = s;
35052         li.addClass("x-menu-sep-li");
35053         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
35054     }
35055 });/*
35056  * Based on:
35057  * Ext JS Library 1.1.1
35058  * Copyright(c) 2006-2007, Ext JS, LLC.
35059  *
35060  * Originally Released Under LGPL - original licence link has changed is not relivant.
35061  *
35062  * Fork - LGPL
35063  * <script type="text/javascript">
35064  */
35065 /**
35066  * @class Roo.menu.Item
35067  * @extends Roo.menu.BaseItem
35068  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
35069  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
35070  * activation and click handling.
35071  * @constructor
35072  * Creates a new Item
35073  * @param {Object} config Configuration options
35074  */
35075 Roo.menu.Item = function(config){
35076     Roo.menu.Item.superclass.constructor.call(this, config);
35077     if(this.menu){
35078         this.menu = Roo.menu.MenuMgr.get(this.menu);
35079     }
35080 };
35081 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
35082     
35083     /**
35084      * @cfg {String} text
35085      * The text to show on the menu item.
35086      */
35087     text: '',
35088      /**
35089      * @cfg {String} HTML to render in menu
35090      * The text to show on the menu item (HTML version).
35091      */
35092     html: '',
35093     /**
35094      * @cfg {String} icon
35095      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
35096      */
35097     icon: undefined,
35098     /**
35099      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
35100      */
35101     itemCls : "x-menu-item",
35102     /**
35103      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
35104      */
35105     canActivate : true,
35106     /**
35107      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
35108      */
35109     showDelay: 200,
35110     // doc'd in BaseItem
35111     hideDelay: 200,
35112
35113     // private
35114     ctype: "Roo.menu.Item",
35115     
35116     // private
35117     onRender : function(container, position){
35118         var el = document.createElement("a");
35119         el.hideFocus = true;
35120         el.unselectable = "on";
35121         el.href = this.href || "#";
35122         if(this.hrefTarget){
35123             el.target = this.hrefTarget;
35124         }
35125         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
35126         
35127         var html = this.html.length ? this.html  : String.format('{0}',this.text);
35128         
35129         el.innerHTML = String.format(
35130                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
35131                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
35132         this.el = el;
35133         Roo.menu.Item.superclass.onRender.call(this, container, position);
35134     },
35135
35136     /**
35137      * Sets the text to display in this menu item
35138      * @param {String} text The text to display
35139      * @param {Boolean} isHTML true to indicate text is pure html.
35140      */
35141     setText : function(text, isHTML){
35142         if (isHTML) {
35143             this.html = text;
35144         } else {
35145             this.text = text;
35146             this.html = '';
35147         }
35148         if(this.rendered){
35149             var html = this.html.length ? this.html  : String.format('{0}',this.text);
35150      
35151             this.el.update(String.format(
35152                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
35153                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
35154             this.parentMenu.autoWidth();
35155         }
35156     },
35157
35158     // private
35159     handleClick : function(e){
35160         if(!this.href){ // if no link defined, stop the event automatically
35161             e.stopEvent();
35162         }
35163         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
35164     },
35165
35166     // private
35167     activate : function(autoExpand){
35168         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
35169             this.focus();
35170             if(autoExpand){
35171                 this.expandMenu();
35172             }
35173         }
35174         return true;
35175     },
35176
35177     // private
35178     shouldDeactivate : function(e){
35179         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
35180             if(this.menu && this.menu.isVisible()){
35181                 return !this.menu.getEl().getRegion().contains(e.getPoint());
35182             }
35183             return true;
35184         }
35185         return false;
35186     },
35187
35188     // private
35189     deactivate : function(){
35190         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
35191         this.hideMenu();
35192     },
35193
35194     // private
35195     expandMenu : function(autoActivate){
35196         if(!this.disabled && this.menu){
35197             clearTimeout(this.hideTimer);
35198             delete this.hideTimer;
35199             if(!this.menu.isVisible() && !this.showTimer){
35200                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
35201             }else if (this.menu.isVisible() && autoActivate){
35202                 this.menu.tryActivate(0, 1);
35203             }
35204         }
35205     },
35206
35207     // private
35208     deferExpand : function(autoActivate){
35209         delete this.showTimer;
35210         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
35211         if(autoActivate){
35212             this.menu.tryActivate(0, 1);
35213         }
35214     },
35215
35216     // private
35217     hideMenu : function(){
35218         clearTimeout(this.showTimer);
35219         delete this.showTimer;
35220         if(!this.hideTimer && this.menu && this.menu.isVisible()){
35221             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
35222         }
35223     },
35224
35225     // private
35226     deferHide : function(){
35227         delete this.hideTimer;
35228         this.menu.hide();
35229     }
35230 });/*
35231  * Based on:
35232  * Ext JS Library 1.1.1
35233  * Copyright(c) 2006-2007, Ext JS, LLC.
35234  *
35235  * Originally Released Under LGPL - original licence link has changed is not relivant.
35236  *
35237  * Fork - LGPL
35238  * <script type="text/javascript">
35239  */
35240  
35241 /**
35242  * @class Roo.menu.CheckItem
35243  * @extends Roo.menu.Item
35244  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
35245  * @constructor
35246  * Creates a new CheckItem
35247  * @param {Object} config Configuration options
35248  */
35249 Roo.menu.CheckItem = function(config){
35250     Roo.menu.CheckItem.superclass.constructor.call(this, config);
35251     this.addEvents({
35252         /**
35253          * @event beforecheckchange
35254          * Fires before the checked value is set, providing an opportunity to cancel if needed
35255          * @param {Roo.menu.CheckItem} this
35256          * @param {Boolean} checked The new checked value that will be set
35257          */
35258         "beforecheckchange" : true,
35259         /**
35260          * @event checkchange
35261          * Fires after the checked value has been set
35262          * @param {Roo.menu.CheckItem} this
35263          * @param {Boolean} checked The checked value that was set
35264          */
35265         "checkchange" : true
35266     });
35267     if(this.checkHandler){
35268         this.on('checkchange', this.checkHandler, this.scope);
35269     }
35270 };
35271 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
35272     /**
35273      * @cfg {String} group
35274      * All check items with the same group name will automatically be grouped into a single-select
35275      * radio button group (defaults to '')
35276      */
35277     /**
35278      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
35279      */
35280     itemCls : "x-menu-item x-menu-check-item",
35281     /**
35282      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
35283      */
35284     groupClass : "x-menu-group-item",
35285
35286     /**
35287      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
35288      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
35289      * initialized with checked = true will be rendered as checked.
35290      */
35291     checked: false,
35292
35293     // private
35294     ctype: "Roo.menu.CheckItem",
35295
35296     // private
35297     onRender : function(c){
35298         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
35299         if(this.group){
35300             this.el.addClass(this.groupClass);
35301         }
35302         Roo.menu.MenuMgr.registerCheckable(this);
35303         if(this.checked){
35304             this.checked = false;
35305             this.setChecked(true, true);
35306         }
35307     },
35308
35309     // private
35310     destroy : function(){
35311         if(this.rendered){
35312             Roo.menu.MenuMgr.unregisterCheckable(this);
35313         }
35314         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
35315     },
35316
35317     /**
35318      * Set the checked state of this item
35319      * @param {Boolean} checked The new checked value
35320      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
35321      */
35322     setChecked : function(state, suppressEvent){
35323         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
35324             if(this.container){
35325                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
35326             }
35327             this.checked = state;
35328             if(suppressEvent !== true){
35329                 this.fireEvent("checkchange", this, state);
35330             }
35331         }
35332     },
35333
35334     // private
35335     handleClick : function(e){
35336        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
35337            this.setChecked(!this.checked);
35338        }
35339        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
35340     }
35341 });/*
35342  * Based on:
35343  * Ext JS Library 1.1.1
35344  * Copyright(c) 2006-2007, Ext JS, LLC.
35345  *
35346  * Originally Released Under LGPL - original licence link has changed is not relivant.
35347  *
35348  * Fork - LGPL
35349  * <script type="text/javascript">
35350  */
35351  
35352 /**
35353  * @class Roo.menu.DateItem
35354  * @extends Roo.menu.Adapter
35355  * A menu item that wraps the {@link Roo.DatPicker} component.
35356  * @constructor
35357  * Creates a new DateItem
35358  * @param {Object} config Configuration options
35359  */
35360 Roo.menu.DateItem = function(config){
35361     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
35362     /** The Roo.DatePicker object @type Roo.DatePicker */
35363     this.picker = this.component;
35364     this.addEvents({select: true});
35365     
35366     this.picker.on("render", function(picker){
35367         picker.getEl().swallowEvent("click");
35368         picker.container.addClass("x-menu-date-item");
35369     });
35370
35371     this.picker.on("select", this.onSelect, this);
35372 };
35373
35374 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
35375     // private
35376     onSelect : function(picker, date){
35377         this.fireEvent("select", this, date, picker);
35378         Roo.menu.DateItem.superclass.handleClick.call(this);
35379     }
35380 });/*
35381  * Based on:
35382  * Ext JS Library 1.1.1
35383  * Copyright(c) 2006-2007, Ext JS, LLC.
35384  *
35385  * Originally Released Under LGPL - original licence link has changed is not relivant.
35386  *
35387  * Fork - LGPL
35388  * <script type="text/javascript">
35389  */
35390  
35391 /**
35392  * @class Roo.menu.ColorItem
35393  * @extends Roo.menu.Adapter
35394  * A menu item that wraps the {@link Roo.ColorPalette} component.
35395  * @constructor
35396  * Creates a new ColorItem
35397  * @param {Object} config Configuration options
35398  */
35399 Roo.menu.ColorItem = function(config){
35400     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
35401     /** The Roo.ColorPalette object @type Roo.ColorPalette */
35402     this.palette = this.component;
35403     this.relayEvents(this.palette, ["select"]);
35404     if(this.selectHandler){
35405         this.on('select', this.selectHandler, this.scope);
35406     }
35407 };
35408 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
35409  * Based on:
35410  * Ext JS Library 1.1.1
35411  * Copyright(c) 2006-2007, Ext JS, LLC.
35412  *
35413  * Originally Released Under LGPL - original licence link has changed is not relivant.
35414  *
35415  * Fork - LGPL
35416  * <script type="text/javascript">
35417  */
35418  
35419
35420 /**
35421  * @class Roo.menu.DateMenu
35422  * @extends Roo.menu.Menu
35423  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
35424  * @constructor
35425  * Creates a new DateMenu
35426  * @param {Object} config Configuration options
35427  */
35428 Roo.menu.DateMenu = function(config){
35429     Roo.menu.DateMenu.superclass.constructor.call(this, config);
35430     this.plain = true;
35431     var di = new Roo.menu.DateItem(config);
35432     this.add(di);
35433     /**
35434      * The {@link Roo.DatePicker} instance for this DateMenu
35435      * @type DatePicker
35436      */
35437     this.picker = di.picker;
35438     /**
35439      * @event select
35440      * @param {DatePicker} picker
35441      * @param {Date} date
35442      */
35443     this.relayEvents(di, ["select"]);
35444
35445     this.on('beforeshow', function(){
35446         if(this.picker){
35447             this.picker.hideMonthPicker(true);
35448         }
35449     }, this);
35450 };
35451 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
35452     cls:'x-date-menu'
35453 });/*
35454  * Based on:
35455  * Ext JS Library 1.1.1
35456  * Copyright(c) 2006-2007, Ext JS, LLC.
35457  *
35458  * Originally Released Under LGPL - original licence link has changed is not relivant.
35459  *
35460  * Fork - LGPL
35461  * <script type="text/javascript">
35462  */
35463  
35464
35465 /**
35466  * @class Roo.menu.ColorMenu
35467  * @extends Roo.menu.Menu
35468  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
35469  * @constructor
35470  * Creates a new ColorMenu
35471  * @param {Object} config Configuration options
35472  */
35473 Roo.menu.ColorMenu = function(config){
35474     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
35475     this.plain = true;
35476     var ci = new Roo.menu.ColorItem(config);
35477     this.add(ci);
35478     /**
35479      * The {@link Roo.ColorPalette} instance for this ColorMenu
35480      * @type ColorPalette
35481      */
35482     this.palette = ci.palette;
35483     /**
35484      * @event select
35485      * @param {ColorPalette} palette
35486      * @param {String} color
35487      */
35488     this.relayEvents(ci, ["select"]);
35489 };
35490 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
35491  * Based on:
35492  * Ext JS Library 1.1.1
35493  * Copyright(c) 2006-2007, Ext JS, LLC.
35494  *
35495  * Originally Released Under LGPL - original licence link has changed is not relivant.
35496  *
35497  * Fork - LGPL
35498  * <script type="text/javascript">
35499  */
35500  
35501 /**
35502  * @class Roo.form.Field
35503  * @extends Roo.BoxComponent
35504  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
35505  * @constructor
35506  * Creates a new Field
35507  * @param {Object} config Configuration options
35508  */
35509 Roo.form.Field = function(config){
35510     Roo.form.Field.superclass.constructor.call(this, config);
35511 };
35512
35513 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
35514     /**
35515      * @cfg {String} fieldLabel Label to use when rendering a form.
35516      */
35517        /**
35518      * @cfg {String} qtip Mouse over tip
35519      */
35520      
35521     /**
35522      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
35523      */
35524     invalidClass : "x-form-invalid",
35525     /**
35526      * @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")
35527      */
35528     invalidText : "The value in this field is invalid",
35529     /**
35530      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
35531      */
35532     focusClass : "x-form-focus",
35533     /**
35534      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
35535       automatic validation (defaults to "keyup").
35536      */
35537     validationEvent : "keyup",
35538     /**
35539      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
35540      */
35541     validateOnBlur : true,
35542     /**
35543      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
35544      */
35545     validationDelay : 250,
35546     /**
35547      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
35548      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
35549      */
35550     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
35551     /**
35552      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
35553      */
35554     fieldClass : "x-form-field",
35555     /**
35556      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
35557      *<pre>
35558 Value         Description
35559 -----------   ----------------------------------------------------------------------
35560 qtip          Display a quick tip when the user hovers over the field
35561 title         Display a default browser title attribute popup
35562 under         Add a block div beneath the field containing the error text
35563 side          Add an error icon to the right of the field with a popup on hover
35564 [element id]  Add the error text directly to the innerHTML of the specified element
35565 </pre>
35566      */
35567     msgTarget : 'qtip',
35568     /**
35569      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
35570      */
35571     msgFx : 'normal',
35572
35573     /**
35574      * @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.
35575      */
35576     readOnly : false,
35577
35578     /**
35579      * @cfg {Boolean} disabled True to disable the field (defaults to false).
35580      */
35581     disabled : false,
35582
35583     /**
35584      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
35585      */
35586     inputType : undefined,
35587     
35588     /**
35589      * @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).
35590          */
35591         tabIndex : undefined,
35592         
35593     // private
35594     isFormField : true,
35595
35596     // private
35597     hasFocus : false,
35598     /**
35599      * @property {Roo.Element} fieldEl
35600      * Element Containing the rendered Field (with label etc.)
35601      */
35602     /**
35603      * @cfg {Mixed} value A value to initialize this field with.
35604      */
35605     value : undefined,
35606
35607     /**
35608      * @cfg {String} name The field's HTML name attribute.
35609      */
35610     /**
35611      * @cfg {String} cls A CSS class to apply to the field's underlying element.
35612      */
35613
35614         // private ??
35615         initComponent : function(){
35616         Roo.form.Field.superclass.initComponent.call(this);
35617         this.addEvents({
35618             /**
35619              * @event focus
35620              * Fires when this field receives input focus.
35621              * @param {Roo.form.Field} this
35622              */
35623             focus : true,
35624             /**
35625              * @event blur
35626              * Fires when this field loses input focus.
35627              * @param {Roo.form.Field} this
35628              */
35629             blur : true,
35630             /**
35631              * @event specialkey
35632              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
35633              * {@link Roo.EventObject#getKey} to determine which key was pressed.
35634              * @param {Roo.form.Field} this
35635              * @param {Roo.EventObject} e The event object
35636              */
35637             specialkey : true,
35638             /**
35639              * @event change
35640              * Fires just before the field blurs if the field value has changed.
35641              * @param {Roo.form.Field} this
35642              * @param {Mixed} newValue The new value
35643              * @param {Mixed} oldValue The original value
35644              */
35645             change : true,
35646             /**
35647              * @event invalid
35648              * Fires after the field has been marked as invalid.
35649              * @param {Roo.form.Field} this
35650              * @param {String} msg The validation message
35651              */
35652             invalid : true,
35653             /**
35654              * @event valid
35655              * Fires after the field has been validated with no errors.
35656              * @param {Roo.form.Field} this
35657              */
35658             valid : true,
35659              /**
35660              * @event keyup
35661              * Fires after the key up
35662              * @param {Roo.form.Field} this
35663              * @param {Roo.EventObject}  e The event Object
35664              */
35665             keyup : true
35666         });
35667     },
35668
35669     /**
35670      * Returns the name attribute of the field if available
35671      * @return {String} name The field name
35672      */
35673     getName: function(){
35674          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
35675     },
35676
35677     // private
35678     onRender : function(ct, position){
35679         Roo.form.Field.superclass.onRender.call(this, ct, position);
35680         if(!this.el){
35681             var cfg = this.getAutoCreate();
35682             if(!cfg.name){
35683                 cfg.name = this.name || this.id;
35684             }
35685             if(this.inputType){
35686                 cfg.type = this.inputType;
35687             }
35688             this.el = ct.createChild(cfg, position);
35689         }
35690         var type = this.el.dom.type;
35691         if(type){
35692             if(type == 'password'){
35693                 type = 'text';
35694             }
35695             this.el.addClass('x-form-'+type);
35696         }
35697         if(this.readOnly){
35698             this.el.dom.readOnly = true;
35699         }
35700         if(this.tabIndex !== undefined){
35701             this.el.dom.setAttribute('tabIndex', this.tabIndex);
35702         }
35703
35704         this.el.addClass([this.fieldClass, this.cls]);
35705         this.initValue();
35706     },
35707
35708     /**
35709      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
35710      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
35711      * @return {Roo.form.Field} this
35712      */
35713     applyTo : function(target){
35714         this.allowDomMove = false;
35715         this.el = Roo.get(target);
35716         this.render(this.el.dom.parentNode);
35717         return this;
35718     },
35719
35720     // private
35721     initValue : function(){
35722         if(this.value !== undefined){
35723             this.setValue(this.value);
35724         }else if(this.el.dom.value.length > 0){
35725             this.setValue(this.el.dom.value);
35726         }
35727     },
35728
35729     /**
35730      * Returns true if this field has been changed since it was originally loaded and is not disabled.
35731      */
35732     isDirty : function() {
35733         if(this.disabled) {
35734             return false;
35735         }
35736         return String(this.getValue()) !== String(this.originalValue);
35737     },
35738
35739     // private
35740     afterRender : function(){
35741         Roo.form.Field.superclass.afterRender.call(this);
35742         this.initEvents();
35743     },
35744
35745     // private
35746     fireKey : function(e){
35747         //Roo.log('field ' + e.getKey());
35748         if(e.isNavKeyPress()){
35749             this.fireEvent("specialkey", this, e);
35750         }
35751     },
35752
35753     /**
35754      * Resets the current field value to the originally loaded value and clears any validation messages
35755      */
35756     reset : function(){
35757         this.setValue(this.originalValue);
35758         this.clearInvalid();
35759     },
35760
35761     // private
35762     initEvents : function(){
35763         // safari killled keypress - so keydown is now used..
35764         this.el.on("keydown" , this.fireKey,  this);
35765         this.el.on("focus", this.onFocus,  this);
35766         this.el.on("blur", this.onBlur,  this);
35767         this.el.relayEvent('keyup', this);
35768
35769         // reference to original value for reset
35770         this.originalValue = this.getValue();
35771     },
35772
35773     // private
35774     onFocus : function(){
35775         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
35776             this.el.addClass(this.focusClass);
35777         }
35778         if(!this.hasFocus){
35779             this.hasFocus = true;
35780             this.startValue = this.getValue();
35781             this.fireEvent("focus", this);
35782         }
35783     },
35784
35785     beforeBlur : Roo.emptyFn,
35786
35787     // private
35788     onBlur : function(){
35789         this.beforeBlur();
35790         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
35791             this.el.removeClass(this.focusClass);
35792         }
35793         this.hasFocus = false;
35794         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
35795             this.validate();
35796         }
35797         var v = this.getValue();
35798         if(String(v) !== String(this.startValue)){
35799             this.fireEvent('change', this, v, this.startValue);
35800         }
35801         this.fireEvent("blur", this);
35802     },
35803
35804     /**
35805      * Returns whether or not the field value is currently valid
35806      * @param {Boolean} preventMark True to disable marking the field invalid
35807      * @return {Boolean} True if the value is valid, else false
35808      */
35809     isValid : function(preventMark){
35810         if(this.disabled){
35811             return true;
35812         }
35813         var restore = this.preventMark;
35814         this.preventMark = preventMark === true;
35815         var v = this.validateValue(this.processValue(this.getRawValue()));
35816         this.preventMark = restore;
35817         return v;
35818     },
35819
35820     /**
35821      * Validates the field value
35822      * @return {Boolean} True if the value is valid, else false
35823      */
35824     validate : function(){
35825         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
35826             this.clearInvalid();
35827             return true;
35828         }
35829         return false;
35830     },
35831
35832     processValue : function(value){
35833         return value;
35834     },
35835
35836     // private
35837     // Subclasses should provide the validation implementation by overriding this
35838     validateValue : function(value){
35839         return true;
35840     },
35841
35842     /**
35843      * Mark this field as invalid
35844      * @param {String} msg The validation message
35845      */
35846     markInvalid : function(msg){
35847         if(!this.rendered || this.preventMark){ // not rendered
35848             return;
35849         }
35850         this.el.addClass(this.invalidClass);
35851         msg = msg || this.invalidText;
35852         switch(this.msgTarget){
35853             case 'qtip':
35854                 this.el.dom.qtip = msg;
35855                 this.el.dom.qclass = 'x-form-invalid-tip';
35856                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
35857                     Roo.QuickTips.enable();
35858                 }
35859                 break;
35860             case 'title':
35861                 this.el.dom.title = msg;
35862                 break;
35863             case 'under':
35864                 if(!this.errorEl){
35865                     var elp = this.el.findParent('.x-form-element', 5, true);
35866                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
35867                     this.errorEl.setWidth(elp.getWidth(true)-20);
35868                 }
35869                 this.errorEl.update(msg);
35870                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
35871                 break;
35872             case 'side':
35873                 if(!this.errorIcon){
35874                     var elp = this.el.findParent('.x-form-element', 5, true);
35875                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
35876                 }
35877                 this.alignErrorIcon();
35878                 this.errorIcon.dom.qtip = msg;
35879                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
35880                 this.errorIcon.show();
35881                 this.on('resize', this.alignErrorIcon, this);
35882                 break;
35883             default:
35884                 var t = Roo.getDom(this.msgTarget);
35885                 t.innerHTML = msg;
35886                 t.style.display = this.msgDisplay;
35887                 break;
35888         }
35889         this.fireEvent('invalid', this, msg);
35890     },
35891
35892     // private
35893     alignErrorIcon : function(){
35894         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
35895     },
35896
35897     /**
35898      * Clear any invalid styles/messages for this field
35899      */
35900     clearInvalid : function(){
35901         if(!this.rendered || this.preventMark){ // not rendered
35902             return;
35903         }
35904         this.el.removeClass(this.invalidClass);
35905         switch(this.msgTarget){
35906             case 'qtip':
35907                 this.el.dom.qtip = '';
35908                 break;
35909             case 'title':
35910                 this.el.dom.title = '';
35911                 break;
35912             case 'under':
35913                 if(this.errorEl){
35914                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
35915                 }
35916                 break;
35917             case 'side':
35918                 if(this.errorIcon){
35919                     this.errorIcon.dom.qtip = '';
35920                     this.errorIcon.hide();
35921                     this.un('resize', this.alignErrorIcon, this);
35922                 }
35923                 break;
35924             default:
35925                 var t = Roo.getDom(this.msgTarget);
35926                 t.innerHTML = '';
35927                 t.style.display = 'none';
35928                 break;
35929         }
35930         this.fireEvent('valid', this);
35931     },
35932
35933     /**
35934      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
35935      * @return {Mixed} value The field value
35936      */
35937     getRawValue : function(){
35938         var v = this.el.getValue();
35939         if(v === this.emptyText){
35940             v = '';
35941         }
35942         return v;
35943     },
35944
35945     /**
35946      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
35947      * @return {Mixed} value The field value
35948      */
35949     getValue : function(){
35950         var v = this.el.getValue();
35951         if(v === this.emptyText || v === undefined){
35952             v = '';
35953         }
35954         return v;
35955     },
35956
35957     /**
35958      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
35959      * @param {Mixed} value The value to set
35960      */
35961     setRawValue : function(v){
35962         return this.el.dom.value = (v === null || v === undefined ? '' : v);
35963     },
35964
35965     /**
35966      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
35967      * @param {Mixed} value The value to set
35968      */
35969     setValue : function(v){
35970         this.value = v;
35971         if(this.rendered){
35972             this.el.dom.value = (v === null || v === undefined ? '' : v);
35973              this.validate();
35974         }
35975     },
35976
35977     adjustSize : function(w, h){
35978         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
35979         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
35980         return s;
35981     },
35982
35983     adjustWidth : function(tag, w){
35984         tag = tag.toLowerCase();
35985         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
35986             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
35987                 if(tag == 'input'){
35988                     return w + 2;
35989                 }
35990                 if(tag = 'textarea'){
35991                     return w-2;
35992                 }
35993             }else if(Roo.isOpera){
35994                 if(tag == 'input'){
35995                     return w + 2;
35996                 }
35997                 if(tag = 'textarea'){
35998                     return w-2;
35999                 }
36000             }
36001         }
36002         return w;
36003     }
36004 });
36005
36006
36007 // anything other than normal should be considered experimental
36008 Roo.form.Field.msgFx = {
36009     normal : {
36010         show: function(msgEl, f){
36011             msgEl.setDisplayed('block');
36012         },
36013
36014         hide : function(msgEl, f){
36015             msgEl.setDisplayed(false).update('');
36016         }
36017     },
36018
36019     slide : {
36020         show: function(msgEl, f){
36021             msgEl.slideIn('t', {stopFx:true});
36022         },
36023
36024         hide : function(msgEl, f){
36025             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
36026         }
36027     },
36028
36029     slideRight : {
36030         show: function(msgEl, f){
36031             msgEl.fixDisplay();
36032             msgEl.alignTo(f.el, 'tl-tr');
36033             msgEl.slideIn('l', {stopFx:true});
36034         },
36035
36036         hide : function(msgEl, f){
36037             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
36038         }
36039     }
36040 };/*
36041  * Based on:
36042  * Ext JS Library 1.1.1
36043  * Copyright(c) 2006-2007, Ext JS, LLC.
36044  *
36045  * Originally Released Under LGPL - original licence link has changed is not relivant.
36046  *
36047  * Fork - LGPL
36048  * <script type="text/javascript">
36049  */
36050  
36051
36052 /**
36053  * @class Roo.form.TextField
36054  * @extends Roo.form.Field
36055  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
36056  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
36057  * @constructor
36058  * Creates a new TextField
36059  * @param {Object} config Configuration options
36060  */
36061 Roo.form.TextField = function(config){
36062     Roo.form.TextField.superclass.constructor.call(this, config);
36063     this.addEvents({
36064         /**
36065          * @event autosize
36066          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
36067          * according to the default logic, but this event provides a hook for the developer to apply additional
36068          * logic at runtime to resize the field if needed.
36069              * @param {Roo.form.Field} this This text field
36070              * @param {Number} width The new field width
36071              */
36072         autosize : true
36073     });
36074 };
36075
36076 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
36077     /**
36078      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
36079      */
36080     grow : false,
36081     /**
36082      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
36083      */
36084     growMin : 30,
36085     /**
36086      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
36087      */
36088     growMax : 800,
36089     /**
36090      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
36091      */
36092     vtype : null,
36093     /**
36094      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
36095      */
36096     maskRe : null,
36097     /**
36098      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
36099      */
36100     disableKeyFilter : false,
36101     /**
36102      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
36103      */
36104     allowBlank : true,
36105     /**
36106      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
36107      */
36108     minLength : 0,
36109     /**
36110      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
36111      */
36112     maxLength : Number.MAX_VALUE,
36113     /**
36114      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
36115      */
36116     minLengthText : "The minimum length for this field is {0}",
36117     /**
36118      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
36119      */
36120     maxLengthText : "The maximum length for this field is {0}",
36121     /**
36122      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
36123      */
36124     selectOnFocus : false,
36125     /**
36126      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
36127      */
36128     blankText : "This field is required",
36129     /**
36130      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
36131      * If available, this function will be called only after the basic validators all return true, and will be passed the
36132      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
36133      */
36134     validator : null,
36135     /**
36136      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
36137      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
36138      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
36139      */
36140     regex : null,
36141     /**
36142      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
36143      */
36144     regexText : "",
36145     /**
36146      * @cfg {String} emptyText The default text to display in an empty field (defaults to null).
36147      */
36148     emptyText : null,
36149     /**
36150      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the {@link #emptyText} (defaults to
36151      * 'x-form-empty-field').  This class is automatically added and removed as needed depending on the current field value.
36152      */
36153     emptyClass : 'x-form-empty-field',
36154
36155     // private
36156     initEvents : function(){
36157         Roo.form.TextField.superclass.initEvents.call(this);
36158         if(this.validationEvent == 'keyup'){
36159             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
36160             this.el.on('keyup', this.filterValidation, this);
36161         }
36162         else if(this.validationEvent !== false){
36163             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
36164         }
36165         if(this.selectOnFocus || this.emptyText){
36166             this.on("focus", this.preFocus, this);
36167             if(this.emptyText){
36168                 this.on('blur', this.postBlur, this);
36169                 this.applyEmptyText();
36170             }
36171         }
36172         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
36173             this.el.on("keypress", this.filterKeys, this);
36174         }
36175         if(this.grow){
36176             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
36177             this.el.on("click", this.autoSize,  this);
36178         }
36179     },
36180
36181     processValue : function(value){
36182         if(this.stripCharsRe){
36183             var newValue = value.replace(this.stripCharsRe, '');
36184             if(newValue !== value){
36185                 this.setRawValue(newValue);
36186                 return newValue;
36187             }
36188         }
36189         return value;
36190     },
36191
36192     filterValidation : function(e){
36193         if(!e.isNavKeyPress()){
36194             this.validationTask.delay(this.validationDelay);
36195         }
36196     },
36197
36198     // private
36199     onKeyUp : function(e){
36200         if(!e.isNavKeyPress()){
36201             this.autoSize();
36202         }
36203     },
36204
36205     /**
36206      * Resets the current field value to the originally-loaded value and clears any validation messages.
36207      * Also adds emptyText and emptyClass if the original value was blank.
36208      */
36209     reset : function(){
36210         Roo.form.TextField.superclass.reset.call(this);
36211         this.applyEmptyText();
36212     },
36213
36214     applyEmptyText : function(){
36215         if(this.rendered && this.emptyText && this.getRawValue().length < 1){
36216             this.setRawValue(this.emptyText);
36217             this.el.addClass(this.emptyClass);
36218         }
36219     },
36220
36221     // private
36222     preFocus : function(){
36223         if(this.emptyText){
36224             if(this.el.dom.value == this.emptyText){
36225                 this.setRawValue('');
36226             }
36227             this.el.removeClass(this.emptyClass);
36228         }
36229         if(this.selectOnFocus){
36230             this.el.dom.select();
36231         }
36232     },
36233
36234     // private
36235     postBlur : function(){
36236         this.applyEmptyText();
36237     },
36238
36239     // private
36240     filterKeys : function(e){
36241         var k = e.getKey();
36242         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
36243             return;
36244         }
36245         var c = e.getCharCode(), cc = String.fromCharCode(c);
36246         if(Roo.isIE && (e.isSpecialKey() || !cc)){
36247             return;
36248         }
36249         if(!this.maskRe.test(cc)){
36250             e.stopEvent();
36251         }
36252     },
36253
36254     setValue : function(v){
36255         if(this.emptyText && this.el && v !== undefined && v !== null && v !== ''){
36256             this.el.removeClass(this.emptyClass);
36257         }
36258         Roo.form.TextField.superclass.setValue.apply(this, arguments);
36259         this.applyEmptyText();
36260         this.autoSize();
36261     },
36262
36263     /**
36264      * Validates a value according to the field's validation rules and marks the field as invalid
36265      * if the validation fails
36266      * @param {Mixed} value The value to validate
36267      * @return {Boolean} True if the value is valid, else false
36268      */
36269     validateValue : function(value){
36270         if(value.length < 1 || value === this.emptyText){ // if it's blank
36271              if(this.allowBlank){
36272                 this.clearInvalid();
36273                 return true;
36274              }else{
36275                 this.markInvalid(this.blankText);
36276                 return false;
36277              }
36278         }
36279         if(value.length < this.minLength){
36280             this.markInvalid(String.format(this.minLengthText, this.minLength));
36281             return false;
36282         }
36283         if(value.length > this.maxLength){
36284             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
36285             return false;
36286         }
36287         if(this.vtype){
36288             var vt = Roo.form.VTypes;
36289             if(!vt[this.vtype](value, this)){
36290                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
36291                 return false;
36292             }
36293         }
36294         if(typeof this.validator == "function"){
36295             var msg = this.validator(value);
36296             if(msg !== true){
36297                 this.markInvalid(msg);
36298                 return false;
36299             }
36300         }
36301         if(this.regex && !this.regex.test(value)){
36302             this.markInvalid(this.regexText);
36303             return false;
36304         }
36305         return true;
36306     },
36307
36308     /**
36309      * Selects text in this field
36310      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
36311      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
36312      */
36313     selectText : function(start, end){
36314         var v = this.getRawValue();
36315         if(v.length > 0){
36316             start = start === undefined ? 0 : start;
36317             end = end === undefined ? v.length : end;
36318             var d = this.el.dom;
36319             if(d.setSelectionRange){
36320                 d.setSelectionRange(start, end);
36321             }else if(d.createTextRange){
36322                 var range = d.createTextRange();
36323                 range.moveStart("character", start);
36324                 range.moveEnd("character", v.length-end);
36325                 range.select();
36326             }
36327         }
36328     },
36329
36330     /**
36331      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
36332      * This only takes effect if grow = true, and fires the autosize event.
36333      */
36334     autoSize : function(){
36335         if(!this.grow || !this.rendered){
36336             return;
36337         }
36338         if(!this.metrics){
36339             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
36340         }
36341         var el = this.el;
36342         var v = el.dom.value;
36343         var d = document.createElement('div');
36344         d.appendChild(document.createTextNode(v));
36345         v = d.innerHTML;
36346         d = null;
36347         v += "&#160;";
36348         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
36349         this.el.setWidth(w);
36350         this.fireEvent("autosize", this, w);
36351     }
36352 });/*
36353  * Based on:
36354  * Ext JS Library 1.1.1
36355  * Copyright(c) 2006-2007, Ext JS, LLC.
36356  *
36357  * Originally Released Under LGPL - original licence link has changed is not relivant.
36358  *
36359  * Fork - LGPL
36360  * <script type="text/javascript">
36361  */
36362  
36363 /**
36364  * @class Roo.form.Hidden
36365  * @extends Roo.form.TextField
36366  * Simple Hidden element used on forms 
36367  * 
36368  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
36369  * 
36370  * @constructor
36371  * Creates a new Hidden form element.
36372  * @param {Object} config Configuration options
36373  */
36374
36375
36376
36377 // easy hidden field...
36378 Roo.form.Hidden = function(config){
36379     Roo.form.Hidden.superclass.constructor.call(this, config);
36380 };
36381   
36382 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
36383     fieldLabel:      '',
36384     inputType:      'hidden',
36385     width:          50,
36386     allowBlank:     true,
36387     labelSeparator: '',
36388     hidden:         true,
36389     itemCls :       'x-form-item-display-none'
36390
36391
36392 });
36393
36394
36395 /*
36396  * Based on:
36397  * Ext JS Library 1.1.1
36398  * Copyright(c) 2006-2007, Ext JS, LLC.
36399  *
36400  * Originally Released Under LGPL - original licence link has changed is not relivant.
36401  *
36402  * Fork - LGPL
36403  * <script type="text/javascript">
36404  */
36405  
36406 /**
36407  * @class Roo.form.TriggerField
36408  * @extends Roo.form.TextField
36409  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
36410  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
36411  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
36412  * for which you can provide a custom implementation.  For example:
36413  * <pre><code>
36414 var trigger = new Roo.form.TriggerField();
36415 trigger.onTriggerClick = myTriggerFn;
36416 trigger.applyTo('my-field');
36417 </code></pre>
36418  *
36419  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
36420  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
36421  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
36422  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
36423  * @constructor
36424  * Create a new TriggerField.
36425  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
36426  * to the base TextField)
36427  */
36428 Roo.form.TriggerField = function(config){
36429     this.mimicing = false;
36430     Roo.form.TriggerField.superclass.constructor.call(this, config);
36431 };
36432
36433 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
36434     /**
36435      * @cfg {String} triggerClass A CSS class to apply to the trigger
36436      */
36437     /**
36438      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36439      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
36440      */
36441     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
36442     /**
36443      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
36444      */
36445     hideTrigger:false,
36446
36447     /** @cfg {Boolean} grow @hide */
36448     /** @cfg {Number} growMin @hide */
36449     /** @cfg {Number} growMax @hide */
36450
36451     /**
36452      * @hide 
36453      * @method
36454      */
36455     autoSize: Roo.emptyFn,
36456     // private
36457     monitorTab : true,
36458     // private
36459     deferHeight : true,
36460
36461     
36462     actionMode : 'wrap',
36463     // private
36464     onResize : function(w, h){
36465         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
36466         if(typeof w == 'number'){
36467             var x = w - this.trigger.getWidth();
36468             this.el.setWidth(this.adjustWidth('input', x));
36469             this.trigger.setStyle('left', x+'px');
36470         }
36471     },
36472
36473     // private
36474     adjustSize : Roo.BoxComponent.prototype.adjustSize,
36475
36476     // private
36477     getResizeEl : function(){
36478         return this.wrap;
36479     },
36480
36481     // private
36482     getPositionEl : function(){
36483         return this.wrap;
36484     },
36485
36486     // private
36487     alignErrorIcon : function(){
36488         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
36489     },
36490
36491     // private
36492     onRender : function(ct, position){
36493         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
36494         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
36495         this.trigger = this.wrap.createChild(this.triggerConfig ||
36496                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
36497         if(this.hideTrigger){
36498             this.trigger.setDisplayed(false);
36499         }
36500         this.initTrigger();
36501         if(!this.width){
36502             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
36503         }
36504     },
36505
36506     // private
36507     initTrigger : function(){
36508         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
36509         this.trigger.addClassOnOver('x-form-trigger-over');
36510         this.trigger.addClassOnClick('x-form-trigger-click');
36511     },
36512
36513     // private
36514     onDestroy : function(){
36515         if(this.trigger){
36516             this.trigger.removeAllListeners();
36517             this.trigger.remove();
36518         }
36519         if(this.wrap){
36520             this.wrap.remove();
36521         }
36522         Roo.form.TriggerField.superclass.onDestroy.call(this);
36523     },
36524
36525     // private
36526     onFocus : function(){
36527         Roo.form.TriggerField.superclass.onFocus.call(this);
36528         if(!this.mimicing){
36529             this.wrap.addClass('x-trigger-wrap-focus');
36530             this.mimicing = true;
36531             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
36532             if(this.monitorTab){
36533                 this.el.on("keydown", this.checkTab, this);
36534             }
36535         }
36536     },
36537
36538     // private
36539     checkTab : function(e){
36540         if(e.getKey() == e.TAB){
36541             this.triggerBlur();
36542         }
36543     },
36544
36545     // private
36546     onBlur : function(){
36547         // do nothing
36548     },
36549
36550     // private
36551     mimicBlur : function(e, t){
36552         if(!this.wrap.contains(t) && this.validateBlur()){
36553             this.triggerBlur();
36554         }
36555     },
36556
36557     // private
36558     triggerBlur : function(){
36559         this.mimicing = false;
36560         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
36561         if(this.monitorTab){
36562             this.el.un("keydown", this.checkTab, this);
36563         }
36564         this.wrap.removeClass('x-trigger-wrap-focus');
36565         Roo.form.TriggerField.superclass.onBlur.call(this);
36566     },
36567
36568     // private
36569     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
36570     validateBlur : function(e, t){
36571         return true;
36572     },
36573
36574     // private
36575     onDisable : function(){
36576         Roo.form.TriggerField.superclass.onDisable.call(this);
36577         if(this.wrap){
36578             this.wrap.addClass('x-item-disabled');
36579         }
36580     },
36581
36582     // private
36583     onEnable : function(){
36584         Roo.form.TriggerField.superclass.onEnable.call(this);
36585         if(this.wrap){
36586             this.wrap.removeClass('x-item-disabled');
36587         }
36588     },
36589
36590     // private
36591     onShow : function(){
36592         var ae = this.getActionEl();
36593         
36594         if(ae){
36595             ae.dom.style.display = '';
36596             ae.dom.style.visibility = 'visible';
36597         }
36598     },
36599
36600     // private
36601     
36602     onHide : function(){
36603         var ae = this.getActionEl();
36604         ae.dom.style.display = 'none';
36605     },
36606
36607     /**
36608      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
36609      * by an implementing function.
36610      * @method
36611      * @param {EventObject} e
36612      */
36613     onTriggerClick : Roo.emptyFn
36614 });
36615
36616 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
36617 // to be extended by an implementing class.  For an example of implementing this class, see the custom
36618 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
36619 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
36620     initComponent : function(){
36621         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
36622
36623         this.triggerConfig = {
36624             tag:'span', cls:'x-form-twin-triggers', cn:[
36625             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
36626             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
36627         ]};
36628     },
36629
36630     getTrigger : function(index){
36631         return this.triggers[index];
36632     },
36633
36634     initTrigger : function(){
36635         var ts = this.trigger.select('.x-form-trigger', true);
36636         this.wrap.setStyle('overflow', 'hidden');
36637         var triggerField = this;
36638         ts.each(function(t, all, index){
36639             t.hide = function(){
36640                 var w = triggerField.wrap.getWidth();
36641                 this.dom.style.display = 'none';
36642                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
36643             };
36644             t.show = function(){
36645                 var w = triggerField.wrap.getWidth();
36646                 this.dom.style.display = '';
36647                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
36648             };
36649             var triggerIndex = 'Trigger'+(index+1);
36650
36651             if(this['hide'+triggerIndex]){
36652                 t.dom.style.display = 'none';
36653             }
36654             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
36655             t.addClassOnOver('x-form-trigger-over');
36656             t.addClassOnClick('x-form-trigger-click');
36657         }, this);
36658         this.triggers = ts.elements;
36659     },
36660
36661     onTrigger1Click : Roo.emptyFn,
36662     onTrigger2Click : Roo.emptyFn
36663 });/*
36664  * Based on:
36665  * Ext JS Library 1.1.1
36666  * Copyright(c) 2006-2007, Ext JS, LLC.
36667  *
36668  * Originally Released Under LGPL - original licence link has changed is not relivant.
36669  *
36670  * Fork - LGPL
36671  * <script type="text/javascript">
36672  */
36673  
36674 /**
36675  * @class Roo.form.TextArea
36676  * @extends Roo.form.TextField
36677  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
36678  * support for auto-sizing.
36679  * @constructor
36680  * Creates a new TextArea
36681  * @param {Object} config Configuration options
36682  */
36683 Roo.form.TextArea = function(config){
36684     Roo.form.TextArea.superclass.constructor.call(this, config);
36685     // these are provided exchanges for backwards compat
36686     // minHeight/maxHeight were replaced by growMin/growMax to be
36687     // compatible with TextField growing config values
36688     if(this.minHeight !== undefined){
36689         this.growMin = this.minHeight;
36690     }
36691     if(this.maxHeight !== undefined){
36692         this.growMax = this.maxHeight;
36693     }
36694 };
36695
36696 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
36697     /**
36698      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
36699      */
36700     growMin : 60,
36701     /**
36702      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
36703      */
36704     growMax: 1000,
36705     /**
36706      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
36707      * in the field (equivalent to setting overflow: hidden, defaults to false)
36708      */
36709     preventScrollbars: false,
36710     /**
36711      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36712      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
36713      */
36714
36715     // private
36716     onRender : function(ct, position){
36717         if(!this.el){
36718             this.defaultAutoCreate = {
36719                 tag: "textarea",
36720                 style:"width:300px;height:60px;",
36721                 autocomplete: "off"
36722             };
36723         }
36724         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
36725         if(this.grow){
36726             this.textSizeEl = Roo.DomHelper.append(document.body, {
36727                 tag: "pre", cls: "x-form-grow-sizer"
36728             });
36729             if(this.preventScrollbars){
36730                 this.el.setStyle("overflow", "hidden");
36731             }
36732             this.el.setHeight(this.growMin);
36733         }
36734     },
36735
36736     onDestroy : function(){
36737         if(this.textSizeEl){
36738             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
36739         }
36740         Roo.form.TextArea.superclass.onDestroy.call(this);
36741     },
36742
36743     // private
36744     onKeyUp : function(e){
36745         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
36746             this.autoSize();
36747         }
36748     },
36749
36750     /**
36751      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
36752      * This only takes effect if grow = true, and fires the autosize event if the height changes.
36753      */
36754     autoSize : function(){
36755         if(!this.grow || !this.textSizeEl){
36756             return;
36757         }
36758         var el = this.el;
36759         var v = el.dom.value;
36760         var ts = this.textSizeEl;
36761
36762         ts.innerHTML = '';
36763         ts.appendChild(document.createTextNode(v));
36764         v = ts.innerHTML;
36765
36766         Roo.fly(ts).setWidth(this.el.getWidth());
36767         if(v.length < 1){
36768             v = "&#160;&#160;";
36769         }else{
36770             if(Roo.isIE){
36771                 v = v.replace(/\n/g, '<p>&#160;</p>');
36772             }
36773             v += "&#160;\n&#160;";
36774         }
36775         ts.innerHTML = v;
36776         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
36777         if(h != this.lastHeight){
36778             this.lastHeight = h;
36779             this.el.setHeight(h);
36780             this.fireEvent("autosize", this, h);
36781         }
36782     }
36783 });/*
36784  * Based on:
36785  * Ext JS Library 1.1.1
36786  * Copyright(c) 2006-2007, Ext JS, LLC.
36787  *
36788  * Originally Released Under LGPL - original licence link has changed is not relivant.
36789  *
36790  * Fork - LGPL
36791  * <script type="text/javascript">
36792  */
36793  
36794
36795 /**
36796  * @class Roo.form.NumberField
36797  * @extends Roo.form.TextField
36798  * Numeric text field that provides automatic keystroke filtering and numeric validation.
36799  * @constructor
36800  * Creates a new NumberField
36801  * @param {Object} config Configuration options
36802  */
36803 Roo.form.NumberField = function(config){
36804     Roo.form.NumberField.superclass.constructor.call(this, config);
36805 };
36806
36807 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
36808     /**
36809      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
36810      */
36811     fieldClass: "x-form-field x-form-num-field",
36812     /**
36813      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36814      */
36815     allowDecimals : true,
36816     /**
36817      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36818      */
36819     decimalSeparator : ".",
36820     /**
36821      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36822      */
36823     decimalPrecision : 2,
36824     /**
36825      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36826      */
36827     allowNegative : true,
36828     /**
36829      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36830      */
36831     minValue : Number.NEGATIVE_INFINITY,
36832     /**
36833      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36834      */
36835     maxValue : Number.MAX_VALUE,
36836     /**
36837      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36838      */
36839     minText : "The minimum value for this field is {0}",
36840     /**
36841      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36842      */
36843     maxText : "The maximum value for this field is {0}",
36844     /**
36845      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36846      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36847      */
36848     nanText : "{0} is not a valid number",
36849
36850     // private
36851     initEvents : function(){
36852         Roo.form.NumberField.superclass.initEvents.call(this);
36853         var allowed = "0123456789";
36854         if(this.allowDecimals){
36855             allowed += this.decimalSeparator;
36856         }
36857         if(this.allowNegative){
36858             allowed += "-";
36859         }
36860         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36861         var keyPress = function(e){
36862             var k = e.getKey();
36863             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36864                 return;
36865             }
36866             var c = e.getCharCode();
36867             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36868                 e.stopEvent();
36869             }
36870         };
36871         this.el.on("keypress", keyPress, this);
36872     },
36873
36874     // private
36875     validateValue : function(value){
36876         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
36877             return false;
36878         }
36879         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
36880              return true;
36881         }
36882         var num = this.parseValue(value);
36883         if(isNaN(num)){
36884             this.markInvalid(String.format(this.nanText, value));
36885             return false;
36886         }
36887         if(num < this.minValue){
36888             this.markInvalid(String.format(this.minText, this.minValue));
36889             return false;
36890         }
36891         if(num > this.maxValue){
36892             this.markInvalid(String.format(this.maxText, this.maxValue));
36893             return false;
36894         }
36895         return true;
36896     },
36897
36898     getValue : function(){
36899         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
36900     },
36901
36902     // private
36903     parseValue : function(value){
36904         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36905         return isNaN(value) ? '' : value;
36906     },
36907
36908     // private
36909     fixPrecision : function(value){
36910         var nan = isNaN(value);
36911         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36912             return nan ? '' : value;
36913         }
36914         return parseFloat(value).toFixed(this.decimalPrecision);
36915     },
36916
36917     setValue : function(v){
36918         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
36919     },
36920
36921     // private
36922     decimalPrecisionFcn : function(v){
36923         return Math.floor(v);
36924     },
36925
36926     beforeBlur : function(){
36927         var v = this.parseValue(this.getRawValue());
36928         if(v){
36929             this.setValue(this.fixPrecision(v));
36930         }
36931     }
36932 });/*
36933  * Based on:
36934  * Ext JS Library 1.1.1
36935  * Copyright(c) 2006-2007, Ext JS, LLC.
36936  *
36937  * Originally Released Under LGPL - original licence link has changed is not relivant.
36938  *
36939  * Fork - LGPL
36940  * <script type="text/javascript">
36941  */
36942  
36943 /**
36944  * @class Roo.form.DateField
36945  * @extends Roo.form.TriggerField
36946  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
36947 * @constructor
36948 * Create a new DateField
36949 * @param {Object} config
36950  */
36951 Roo.form.DateField = function(config){
36952     Roo.form.DateField.superclass.constructor.call(this, config);
36953     
36954       this.addEvents({
36955          
36956         /**
36957          * @event select
36958          * Fires when a date is selected
36959              * @param {Roo.form.DateField} combo This combo box
36960              * @param {Date} date The date selected
36961              */
36962         'select' : true
36963          
36964     });
36965     
36966     
36967     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
36968     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
36969     this.ddMatch = null;
36970     if(this.disabledDates){
36971         var dd = this.disabledDates;
36972         var re = "(?:";
36973         for(var i = 0; i < dd.length; i++){
36974             re += dd[i];
36975             if(i != dd.length-1) re += "|";
36976         }
36977         this.ddMatch = new RegExp(re + ")");
36978     }
36979 };
36980
36981 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
36982     /**
36983      * @cfg {String} format
36984      * The default date format string which can be overriden for localization support.  The format must be
36985      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
36986      */
36987     format : "m/d/y",
36988     /**
36989      * @cfg {String} altFormats
36990      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
36991      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
36992      */
36993     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
36994     /**
36995      * @cfg {Array} disabledDays
36996      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
36997      */
36998     disabledDays : null,
36999     /**
37000      * @cfg {String} disabledDaysText
37001      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
37002      */
37003     disabledDaysText : "Disabled",
37004     /**
37005      * @cfg {Array} disabledDates
37006      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
37007      * expression so they are very powerful. Some examples:
37008      * <ul>
37009      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
37010      * <li>["03/08", "09/16"] would disable those days for every year</li>
37011      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
37012      * <li>["03/../2006"] would disable every day in March 2006</li>
37013      * <li>["^03"] would disable every day in every March</li>
37014      * </ul>
37015      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
37016      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
37017      */
37018     disabledDates : null,
37019     /**
37020      * @cfg {String} disabledDatesText
37021      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
37022      */
37023     disabledDatesText : "Disabled",
37024     /**
37025      * @cfg {Date/String} minValue
37026      * The minimum allowed date. Can be either a Javascript date object or a string date in a
37027      * valid format (defaults to null).
37028      */
37029     minValue : null,
37030     /**
37031      * @cfg {Date/String} maxValue
37032      * The maximum allowed date. Can be either a Javascript date object or a string date in a
37033      * valid format (defaults to null).
37034      */
37035     maxValue : null,
37036     /**
37037      * @cfg {String} minText
37038      * The error text to display when the date in the cell is before minValue (defaults to
37039      * 'The date in this field must be after {minValue}').
37040      */
37041     minText : "The date in this field must be equal to or after {0}",
37042     /**
37043      * @cfg {String} maxText
37044      * The error text to display when the date in the cell is after maxValue (defaults to
37045      * 'The date in this field must be before {maxValue}').
37046      */
37047     maxText : "The date in this field must be equal to or before {0}",
37048     /**
37049      * @cfg {String} invalidText
37050      * The error text to display when the date in the field is invalid (defaults to
37051      * '{value} is not a valid date - it must be in the format {format}').
37052      */
37053     invalidText : "{0} is not a valid date - it must be in the format {1}",
37054     /**
37055      * @cfg {String} triggerClass
37056      * An additional CSS class used to style the trigger button.  The trigger will always get the
37057      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
37058      * which displays a calendar icon).
37059      */
37060     triggerClass : 'x-form-date-trigger',
37061     
37062
37063     /**
37064      * @cfg {bool} useIso
37065      * if enabled, then the date field will use a hidden field to store the 
37066      * real value as iso formated date. default (false)
37067      */ 
37068     useIso : false,
37069     /**
37070      * @cfg {String/Object} autoCreate
37071      * A DomHelper element spec, or true for a default element spec (defaults to
37072      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
37073      */ 
37074     // private
37075     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
37076     
37077     // private
37078     hiddenField: false,
37079     
37080     onRender : function(ct, position)
37081     {
37082         Roo.form.DateField.superclass.onRender.call(this, ct, position);
37083         if (this.useIso) {
37084             this.el.dom.removeAttribute('name'); 
37085             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
37086                     'before', true);
37087             this.hiddenField.value = this.formatDate(this.value, 'Y-m-d');
37088             // prevent input submission
37089             this.hiddenName = this.name;
37090         }
37091             
37092             
37093     },
37094     
37095     // private
37096     validateValue : function(value)
37097     {
37098         value = this.formatDate(value);
37099         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
37100             return false;
37101         }
37102         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
37103              return true;
37104         }
37105         var svalue = value;
37106         value = this.parseDate(value);
37107         if(!value){
37108             this.markInvalid(String.format(this.invalidText, svalue, this.format));
37109             return false;
37110         }
37111         var time = value.getTime();
37112         if(this.minValue && time < this.minValue.getTime()){
37113             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
37114             return false;
37115         }
37116         if(this.maxValue && time > this.maxValue.getTime()){
37117             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
37118             return false;
37119         }
37120         if(this.disabledDays){
37121             var day = value.getDay();
37122             for(var i = 0; i < this.disabledDays.length; i++) {
37123                 if(day === this.disabledDays[i]){
37124                     this.markInvalid(this.disabledDaysText);
37125                     return false;
37126                 }
37127             }
37128         }
37129         var fvalue = this.formatDate(value);
37130         if(this.ddMatch && this.ddMatch.test(fvalue)){
37131             this.markInvalid(String.format(this.disabledDatesText, fvalue));
37132             return false;
37133         }
37134         return true;
37135     },
37136
37137     // private
37138     // Provides logic to override the default TriggerField.validateBlur which just returns true
37139     validateBlur : function(){
37140         return !this.menu || !this.menu.isVisible();
37141     },
37142
37143     /**
37144      * Returns the current date value of the date field.
37145      * @return {Date} The date value
37146      */
37147     getValue : function(){
37148         
37149         return  this.hiddenField ? this.hiddenField.value : this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
37150     },
37151
37152     /**
37153      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
37154      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
37155      * (the default format used is "m/d/y").
37156      * <br />Usage:
37157      * <pre><code>
37158 //All of these calls set the same date value (May 4, 2006)
37159
37160 //Pass a date object:
37161 var dt = new Date('5/4/06');
37162 dateField.setValue(dt);
37163
37164 //Pass a date string (default format):
37165 dateField.setValue('5/4/06');
37166
37167 //Pass a date string (custom format):
37168 dateField.format = 'Y-m-d';
37169 dateField.setValue('2006-5-4');
37170 </code></pre>
37171      * @param {String/Date} date The date or valid date string
37172      */
37173     setValue : function(date){
37174         if (this.hiddenField) {
37175             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
37176         }
37177         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
37178     },
37179
37180     // private
37181     parseDate : function(value){
37182         if(!value || value instanceof Date){
37183             return value;
37184         }
37185         var v = Date.parseDate(value, this.format);
37186         if(!v && this.altFormats){
37187             if(!this.altFormatsArray){
37188                 this.altFormatsArray = this.altFormats.split("|");
37189             }
37190             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
37191                 v = Date.parseDate(value, this.altFormatsArray[i]);
37192             }
37193         }
37194         return v;
37195     },
37196
37197     // private
37198     formatDate : function(date, fmt){
37199         return (!date || !(date instanceof Date)) ?
37200                date : date.dateFormat(fmt || this.format);
37201     },
37202
37203     // private
37204     menuListeners : {
37205         select: function(m, d){
37206             this.setValue(d);
37207             this.fireEvent('select', this, d);
37208         },
37209         show : function(){ // retain focus styling
37210             this.onFocus();
37211         },
37212         hide : function(){
37213             this.focus.defer(10, this);
37214             var ml = this.menuListeners;
37215             this.menu.un("select", ml.select,  this);
37216             this.menu.un("show", ml.show,  this);
37217             this.menu.un("hide", ml.hide,  this);
37218         }
37219     },
37220
37221     // private
37222     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
37223     onTriggerClick : function(){
37224         if(this.disabled){
37225             return;
37226         }
37227         if(this.menu == null){
37228             this.menu = new Roo.menu.DateMenu();
37229         }
37230         Roo.apply(this.menu.picker,  {
37231             showClear: this.allowBlank,
37232             minDate : this.minValue,
37233             maxDate : this.maxValue,
37234             disabledDatesRE : this.ddMatch,
37235             disabledDatesText : this.disabledDatesText,
37236             disabledDays : this.disabledDays,
37237             disabledDaysText : this.disabledDaysText,
37238             format : this.format,
37239             minText : String.format(this.minText, this.formatDate(this.minValue)),
37240             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
37241         });
37242         this.menu.on(Roo.apply({}, this.menuListeners, {
37243             scope:this
37244         }));
37245         this.menu.picker.setValue(this.getValue() || new Date());
37246         this.menu.show(this.el, "tl-bl?");
37247     },
37248
37249     beforeBlur : function(){
37250         var v = this.parseDate(this.getRawValue());
37251         if(v){
37252             this.setValue(v);
37253         }
37254     }
37255
37256     /** @cfg {Boolean} grow @hide */
37257     /** @cfg {Number} growMin @hide */
37258     /** @cfg {Number} growMax @hide */
37259     /**
37260      * @hide
37261      * @method autoSize
37262      */
37263 });/*
37264  * Based on:
37265  * Ext JS Library 1.1.1
37266  * Copyright(c) 2006-2007, Ext JS, LLC.
37267  *
37268  * Originally Released Under LGPL - original licence link has changed is not relivant.
37269  *
37270  * Fork - LGPL
37271  * <script type="text/javascript">
37272  */
37273  
37274
37275 /**
37276  * @class Roo.form.ComboBox
37277  * @extends Roo.form.TriggerField
37278  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
37279  * @constructor
37280  * Create a new ComboBox.
37281  * @param {Object} config Configuration options
37282  */
37283 Roo.form.ComboBox = function(config){
37284     Roo.form.ComboBox.superclass.constructor.call(this, config);
37285     this.addEvents({
37286         /**
37287          * @event expand
37288          * Fires when the dropdown list is expanded
37289              * @param {Roo.form.ComboBox} combo This combo box
37290              */
37291         'expand' : true,
37292         /**
37293          * @event collapse
37294          * Fires when the dropdown list is collapsed
37295              * @param {Roo.form.ComboBox} combo This combo box
37296              */
37297         'collapse' : true,
37298         /**
37299          * @event beforeselect
37300          * Fires before a list item is selected. Return false to cancel the selection.
37301              * @param {Roo.form.ComboBox} combo This combo box
37302              * @param {Roo.data.Record} record The data record returned from the underlying store
37303              * @param {Number} index The index of the selected item in the dropdown list
37304              */
37305         'beforeselect' : true,
37306         /**
37307          * @event select
37308          * Fires when a list item is selected
37309              * @param {Roo.form.ComboBox} combo This combo box
37310              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
37311              * @param {Number} index The index of the selected item in the dropdown list
37312              */
37313         'select' : true,
37314         /**
37315          * @event beforequery
37316          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
37317          * The event object passed has these properties:
37318              * @param {Roo.form.ComboBox} combo This combo box
37319              * @param {String} query The query
37320              * @param {Boolean} forceAll true to force "all" query
37321              * @param {Boolean} cancel true to cancel the query
37322              * @param {Object} e The query event object
37323              */
37324         'beforequery': true,
37325          /**
37326          * @event add
37327          * Fires when the 'add' icon is pressed (add a listener to enable add button)
37328              * @param {Roo.form.ComboBox} combo This combo box
37329              */
37330         'add' : true,
37331         /**
37332          * @event edit
37333          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
37334              * @param {Roo.form.ComboBox} combo This combo box
37335              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
37336              */
37337         'edit' : true
37338         
37339         
37340     });
37341     if(this.transform){
37342         this.allowDomMove = false;
37343         var s = Roo.getDom(this.transform);
37344         if(!this.hiddenName){
37345             this.hiddenName = s.name;
37346         }
37347         if(!this.store){
37348             this.mode = 'local';
37349             var d = [], opts = s.options;
37350             for(var i = 0, len = opts.length;i < len; i++){
37351                 var o = opts[i];
37352                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
37353                 if(o.selected) {
37354                     this.value = value;
37355                 }
37356                 d.push([value, o.text]);
37357             }
37358             this.store = new Roo.data.SimpleStore({
37359                 'id': 0,
37360                 fields: ['value', 'text'],
37361                 data : d
37362             });
37363             this.valueField = 'value';
37364             this.displayField = 'text';
37365         }
37366         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
37367         if(!this.lazyRender){
37368             this.target = true;
37369             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
37370             s.parentNode.removeChild(s); // remove it
37371             this.render(this.el.parentNode);
37372         }else{
37373             s.parentNode.removeChild(s); // remove it
37374         }
37375
37376     }
37377     if (this.store) {
37378         this.store = Roo.factory(this.store, Roo.data);
37379     }
37380     
37381     this.selectedIndex = -1;
37382     if(this.mode == 'local'){
37383         if(config.queryDelay === undefined){
37384             this.queryDelay = 10;
37385         }
37386         if(config.minChars === undefined){
37387             this.minChars = 0;
37388         }
37389     }
37390 };
37391
37392 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
37393     /**
37394      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
37395      */
37396     /**
37397      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
37398      * rendering into an Roo.Editor, defaults to false)
37399      */
37400     /**
37401      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
37402      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
37403      */
37404     /**
37405      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
37406      */
37407     /**
37408      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
37409      * the dropdown list (defaults to undefined, with no header element)
37410      */
37411
37412      /**
37413      * @cfg {String/Roo.Template} tpl The template to use to render the output
37414      */
37415      
37416     // private
37417     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
37418     /**
37419      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
37420      */
37421     listWidth: undefined,
37422     /**
37423      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
37424      * mode = 'remote' or 'text' if mode = 'local')
37425      */
37426     displayField: undefined,
37427     /**
37428      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
37429      * mode = 'remote' or 'value' if mode = 'local'). 
37430      * Note: use of a valueField requires the user make a selection
37431      * in order for a value to be mapped.
37432      */
37433     valueField: undefined,
37434     
37435     
37436     /**
37437      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
37438      * field's data value (defaults to the underlying DOM element's name)
37439      */
37440     hiddenName: undefined,
37441     /**
37442      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
37443      */
37444     listClass: '',
37445     /**
37446      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
37447      */
37448     selectedClass: 'x-combo-selected',
37449     /**
37450      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
37451      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
37452      * which displays a downward arrow icon).
37453      */
37454     triggerClass : 'x-form-arrow-trigger',
37455     /**
37456      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
37457      */
37458     shadow:'sides',
37459     /**
37460      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
37461      * anchor positions (defaults to 'tl-bl')
37462      */
37463     listAlign: 'tl-bl?',
37464     /**
37465      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
37466      */
37467     maxHeight: 300,
37468     /**
37469      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
37470      * query specified by the allQuery config option (defaults to 'query')
37471      */
37472     triggerAction: 'query',
37473     /**
37474      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
37475      * (defaults to 4, does not apply if editable = false)
37476      */
37477     minChars : 4,
37478     /**
37479      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
37480      * delay (typeAheadDelay) if it matches a known value (defaults to false)
37481      */
37482     typeAhead: false,
37483     /**
37484      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
37485      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
37486      */
37487     queryDelay: 500,
37488     /**
37489      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
37490      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
37491      */
37492     pageSize: 0,
37493     /**
37494      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
37495      * when editable = true (defaults to false)
37496      */
37497     selectOnFocus:false,
37498     /**
37499      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
37500      */
37501     queryParam: 'query',
37502     /**
37503      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
37504      * when mode = 'remote' (defaults to 'Loading...')
37505      */
37506     loadingText: 'Loading...',
37507     /**
37508      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
37509      */
37510     resizable: false,
37511     /**
37512      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
37513      */
37514     handleHeight : 8,
37515     /**
37516      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
37517      * traditional select (defaults to true)
37518      */
37519     editable: true,
37520     /**
37521      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
37522      */
37523     allQuery: '',
37524     /**
37525      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
37526      */
37527     mode: 'remote',
37528     /**
37529      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
37530      * listWidth has a higher value)
37531      */
37532     minListWidth : 70,
37533     /**
37534      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
37535      * allow the user to set arbitrary text into the field (defaults to false)
37536      */
37537     forceSelection:false,
37538     /**
37539      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
37540      * if typeAhead = true (defaults to 250)
37541      */
37542     typeAheadDelay : 250,
37543     /**
37544      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
37545      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
37546      */
37547     valueNotFoundText : undefined,
37548     /**
37549      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
37550      */
37551     blockFocus : false,
37552     
37553     /**
37554      * @cfg {Boolean} disableClear Disable showing of clear button.
37555      */
37556     disableClear : false,
37557     /**
37558      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
37559      */
37560     alwaysQuery : false,
37561     
37562     //private
37563     addicon : false,
37564     editicon: false,
37565     
37566     // element that contains real text value.. (when hidden is used..)
37567      
37568     // private
37569     onRender : function(ct, position){
37570         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
37571         if(this.hiddenName){
37572             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
37573                     'before', true);
37574             this.hiddenField.value =
37575                 this.hiddenValue !== undefined ? this.hiddenValue :
37576                 this.value !== undefined ? this.value : '';
37577
37578             // prevent input submission
37579             this.el.dom.removeAttribute('name');
37580              
37581              
37582         }
37583         if(Roo.isGecko){
37584             this.el.dom.setAttribute('autocomplete', 'off');
37585         }
37586
37587         var cls = 'x-combo-list';
37588
37589         this.list = new Roo.Layer({
37590             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
37591         });
37592
37593         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
37594         this.list.setWidth(lw);
37595         this.list.swallowEvent('mousewheel');
37596         this.assetHeight = 0;
37597
37598         if(this.title){
37599             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
37600             this.assetHeight += this.header.getHeight();
37601         }
37602
37603         this.innerList = this.list.createChild({cls:cls+'-inner'});
37604         this.innerList.on('mouseover', this.onViewOver, this);
37605         this.innerList.on('mousemove', this.onViewMove, this);
37606         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
37607         
37608         if(this.allowBlank && !this.pageSize && !this.disableClear){
37609             this.footer = this.list.createChild({cls:cls+'-ft'});
37610             this.pageTb = new Roo.Toolbar(this.footer);
37611            
37612         }
37613         if(this.pageSize){
37614             this.footer = this.list.createChild({cls:cls+'-ft'});
37615             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
37616                     {pageSize: this.pageSize});
37617             
37618         }
37619         
37620         if (this.pageTb && this.allowBlank && !this.disableClear) {
37621             var _this = this;
37622             this.pageTb.add(new Roo.Toolbar.Fill(), {
37623                 cls: 'x-btn-icon x-btn-clear',
37624                 text: '&#160;',
37625                 handler: function()
37626                 {
37627                     _this.collapse();
37628                     _this.clearValue();
37629                     _this.onSelect(false, -1);
37630                 }
37631             });
37632         }
37633         if (this.footer) {
37634             this.assetHeight += this.footer.getHeight();
37635         }
37636         
37637
37638         if(!this.tpl){
37639             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
37640         }
37641
37642         this.view = new Roo.View(this.innerList, this.tpl, {
37643             singleSelect:true, store: this.store, selectedClass: this.selectedClass
37644         });
37645
37646         this.view.on('click', this.onViewClick, this);
37647
37648         this.store.on('beforeload', this.onBeforeLoad, this);
37649         this.store.on('load', this.onLoad, this);
37650         this.store.on('loadexception', this.onLoadException, this);
37651
37652         if(this.resizable){
37653             this.resizer = new Roo.Resizable(this.list,  {
37654                pinned:true, handles:'se'
37655             });
37656             this.resizer.on('resize', function(r, w, h){
37657                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
37658                 this.listWidth = w;
37659                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
37660                 this.restrictHeight();
37661             }, this);
37662             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
37663         }
37664         if(!this.editable){
37665             this.editable = true;
37666             this.setEditable(false);
37667         }  
37668         
37669         
37670         if (typeof(this.events.add.listeners) != 'undefined') {
37671             
37672             this.addicon = this.wrap.createChild(
37673                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
37674        
37675             this.addicon.on('click', function(e) {
37676                 this.fireEvent('add', this);
37677             }, this);
37678         }
37679         if (typeof(this.events.edit.listeners) != 'undefined') {
37680             
37681             this.editicon = this.wrap.createChild(
37682                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
37683             if (this.addicon) {
37684                 this.editicon.setStyle('margin-left', '40px');
37685             }
37686             this.editicon.on('click', function(e) {
37687                 
37688                 // we fire even  if inothing is selected..
37689                 this.fireEvent('edit', this, this.lastData );
37690                 
37691             }, this);
37692         }
37693         
37694         
37695         
37696     },
37697
37698     // private
37699     initEvents : function(){
37700         Roo.form.ComboBox.superclass.initEvents.call(this);
37701
37702         this.keyNav = new Roo.KeyNav(this.el, {
37703             "up" : function(e){
37704                 this.inKeyMode = true;
37705                 this.selectPrev();
37706             },
37707
37708             "down" : function(e){
37709                 if(!this.isExpanded()){
37710                     this.onTriggerClick();
37711                 }else{
37712                     this.inKeyMode = true;
37713                     this.selectNext();
37714                 }
37715             },
37716
37717             "enter" : function(e){
37718                 this.onViewClick();
37719                 //return true;
37720             },
37721
37722             "esc" : function(e){
37723                 this.collapse();
37724             },
37725
37726             "tab" : function(e){
37727                 this.onViewClick(false);
37728                 this.fireEvent("specialkey", this, e);
37729                 return true;
37730             },
37731
37732             scope : this,
37733
37734             doRelay : function(foo, bar, hname){
37735                 if(hname == 'down' || this.scope.isExpanded()){
37736                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
37737                 }
37738                 return true;
37739             },
37740
37741             forceKeyDown: true
37742         });
37743         this.queryDelay = Math.max(this.queryDelay || 10,
37744                 this.mode == 'local' ? 10 : 250);
37745         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
37746         if(this.typeAhead){
37747             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
37748         }
37749         if(this.editable !== false){
37750             this.el.on("keyup", this.onKeyUp, this);
37751         }
37752         if(this.forceSelection){
37753             this.on('blur', this.doForce, this);
37754         }
37755     },
37756
37757     onDestroy : function(){
37758         if(this.view){
37759             this.view.setStore(null);
37760             this.view.el.removeAllListeners();
37761             this.view.el.remove();
37762             this.view.purgeListeners();
37763         }
37764         if(this.list){
37765             this.list.destroy();
37766         }
37767         if(this.store){
37768             this.store.un('beforeload', this.onBeforeLoad, this);
37769             this.store.un('load', this.onLoad, this);
37770             this.store.un('loadexception', this.onLoadException, this);
37771         }
37772         Roo.form.ComboBox.superclass.onDestroy.call(this);
37773     },
37774
37775     // private
37776     fireKey : function(e){
37777         if(e.isNavKeyPress() && !this.list.isVisible()){
37778             this.fireEvent("specialkey", this, e);
37779         }
37780     },
37781
37782     // private
37783     onResize: function(w, h){
37784         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
37785         
37786         if(typeof w != 'number'){
37787             // we do not handle it!?!?
37788             return;
37789         }
37790         var tw = this.trigger.getWidth();
37791         tw += this.addicon ? this.addicon.getWidth() : 0;
37792         tw += this.editicon ? this.editicon.getWidth() : 0;
37793         var x = w - tw;
37794         this.el.setWidth( this.adjustWidth('input', x));
37795             
37796         this.trigger.setStyle('left', x+'px');
37797         
37798         if(this.list && this.listWidth === undefined){
37799             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
37800             this.list.setWidth(lw);
37801             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
37802         }
37803         
37804     
37805         
37806     },
37807
37808     /**
37809      * Allow or prevent the user from directly editing the field text.  If false is passed,
37810      * the user will only be able to select from the items defined in the dropdown list.  This method
37811      * is the runtime equivalent of setting the 'editable' config option at config time.
37812      * @param {Boolean} value True to allow the user to directly edit the field text
37813      */
37814     setEditable : function(value){
37815         if(value == this.editable){
37816             return;
37817         }
37818         this.editable = value;
37819         if(!value){
37820             this.el.dom.setAttribute('readOnly', true);
37821             this.el.on('mousedown', this.onTriggerClick,  this);
37822             this.el.addClass('x-combo-noedit');
37823         }else{
37824             this.el.dom.setAttribute('readOnly', false);
37825             this.el.un('mousedown', this.onTriggerClick,  this);
37826             this.el.removeClass('x-combo-noedit');
37827         }
37828     },
37829
37830     // private
37831     onBeforeLoad : function(){
37832         if(!this.hasFocus){
37833             return;
37834         }
37835         this.innerList.update(this.loadingText ?
37836                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
37837         this.restrictHeight();
37838         this.selectedIndex = -1;
37839     },
37840
37841     // private
37842     onLoad : function(){
37843         if(!this.hasFocus){
37844             return;
37845         }
37846         if(this.store.getCount() > 0){
37847             this.expand();
37848             this.restrictHeight();
37849             if(this.lastQuery == this.allQuery){
37850                 if(this.editable){
37851                     this.el.dom.select();
37852                 }
37853                 if(!this.selectByValue(this.value, true)){
37854                     this.select(0, true);
37855                 }
37856             }else{
37857                 this.selectNext();
37858                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
37859                     this.taTask.delay(this.typeAheadDelay);
37860                 }
37861             }
37862         }else{
37863             this.onEmptyResults();
37864         }
37865         //this.el.focus();
37866     },
37867     // private
37868     onLoadException : function()
37869     {
37870         this.collapse();
37871         Roo.log(this.store.reader.jsonData);
37872         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
37873             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
37874         }
37875         
37876         
37877     },
37878     // private
37879     onTypeAhead : function(){
37880         if(this.store.getCount() > 0){
37881             var r = this.store.getAt(0);
37882             var newValue = r.data[this.displayField];
37883             var len = newValue.length;
37884             var selStart = this.getRawValue().length;
37885             if(selStart != len){
37886                 this.setRawValue(newValue);
37887                 this.selectText(selStart, newValue.length);
37888             }
37889         }
37890     },
37891
37892     // private
37893     onSelect : function(record, index){
37894         if(this.fireEvent('beforeselect', this, record, index) !== false){
37895             this.setFromData(index > -1 ? record.data : false);
37896             this.collapse();
37897             this.fireEvent('select', this, record, index);
37898         }
37899     },
37900
37901     /**
37902      * Returns the currently selected field value or empty string if no value is set.
37903      * @return {String} value The selected value
37904      */
37905     getValue : function(){
37906         if(this.valueField){
37907             return typeof this.value != 'undefined' ? this.value : '';
37908         }else{
37909             return Roo.form.ComboBox.superclass.getValue.call(this);
37910         }
37911     },
37912
37913     /**
37914      * Clears any text/value currently set in the field
37915      */
37916     clearValue : function(){
37917         if(this.hiddenField){
37918             this.hiddenField.value = '';
37919         }
37920         this.value = '';
37921         this.setRawValue('');
37922         this.lastSelectionText = '';
37923         this.applyEmptyText();
37924     },
37925
37926     /**
37927      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
37928      * will be displayed in the field.  If the value does not match the data value of an existing item,
37929      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
37930      * Otherwise the field will be blank (although the value will still be set).
37931      * @param {String} value The value to match
37932      */
37933     setValue : function(v){
37934         var text = v;
37935         if(this.valueField){
37936             var r = this.findRecord(this.valueField, v);
37937             if(r){
37938                 text = r.data[this.displayField];
37939             }else if(this.valueNotFoundText !== undefined){
37940                 text = this.valueNotFoundText;
37941             }
37942         }
37943         this.lastSelectionText = text;
37944         if(this.hiddenField){
37945             this.hiddenField.value = v;
37946         }
37947         Roo.form.ComboBox.superclass.setValue.call(this, text);
37948         this.value = v;
37949     },
37950     /**
37951      * @property {Object} the last set data for the element
37952      */
37953     
37954     lastData : false,
37955     /**
37956      * Sets the value of the field based on a object which is related to the record format for the store.
37957      * @param {Object} value the value to set as. or false on reset?
37958      */
37959     setFromData : function(o){
37960         var dv = ''; // display value
37961         var vv = ''; // value value..
37962         this.lastData = o;
37963         if (this.displayField) {
37964             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
37965         } else {
37966             // this is an error condition!!!
37967             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
37968         }
37969         
37970         if(this.valueField){
37971             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
37972         }
37973         if(this.hiddenField){
37974             this.hiddenField.value = vv;
37975             
37976             this.lastSelectionText = dv;
37977             Roo.form.ComboBox.superclass.setValue.call(this, dv);
37978             this.value = vv;
37979             return;
37980         }
37981         // no hidden field.. - we store the value in 'value', but still display
37982         // display field!!!!
37983         this.lastSelectionText = dv;
37984         Roo.form.ComboBox.superclass.setValue.call(this, dv);
37985         this.value = vv;
37986         
37987         
37988     },
37989     // private
37990     reset : function(){
37991         // overridden so that last data is reset..
37992         this.setValue(this.originalValue);
37993         this.clearInvalid();
37994         this.lastData = false;
37995     },
37996     // private
37997     findRecord : function(prop, value){
37998         var record;
37999         if(this.store.getCount() > 0){
38000             this.store.each(function(r){
38001                 if(r.data[prop] == value){
38002                     record = r;
38003                     return false;
38004                 }
38005                 return true;
38006             });
38007         }
38008         return record;
38009     },
38010     
38011     getName: function()
38012     {
38013         // returns hidden if it's set..
38014         if (!this.rendered) {return ''};
38015         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
38016         
38017     },
38018     // private
38019     onViewMove : function(e, t){
38020         this.inKeyMode = false;
38021     },
38022
38023     // private
38024     onViewOver : function(e, t){
38025         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
38026             return;
38027         }
38028         var item = this.view.findItemFromChild(t);
38029         if(item){
38030             var index = this.view.indexOf(item);
38031             this.select(index, false);
38032         }
38033     },
38034
38035     // private
38036     onViewClick : function(doFocus)
38037     {
38038         var index = this.view.getSelectedIndexes()[0];
38039         var r = this.store.getAt(index);
38040         if(r){
38041             this.onSelect(r, index);
38042         }
38043         if(doFocus !== false && !this.blockFocus){
38044             this.el.focus();
38045         }
38046     },
38047
38048     // private
38049     restrictHeight : function(){
38050         this.innerList.dom.style.height = '';
38051         var inner = this.innerList.dom;
38052         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
38053         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
38054         this.list.beginUpdate();
38055         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
38056         this.list.alignTo(this.el, this.listAlign);
38057         this.list.endUpdate();
38058     },
38059
38060     // private
38061     onEmptyResults : function(){
38062         this.collapse();
38063     },
38064
38065     /**
38066      * Returns true if the dropdown list is expanded, else false.
38067      */
38068     isExpanded : function(){
38069         return this.list.isVisible();
38070     },
38071
38072     /**
38073      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
38074      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
38075      * @param {String} value The data value of the item to select
38076      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
38077      * selected item if it is not currently in view (defaults to true)
38078      * @return {Boolean} True if the value matched an item in the list, else false
38079      */
38080     selectByValue : function(v, scrollIntoView){
38081         if(v !== undefined && v !== null){
38082             var r = this.findRecord(this.valueField || this.displayField, v);
38083             if(r){
38084                 this.select(this.store.indexOf(r), scrollIntoView);
38085                 return true;
38086             }
38087         }
38088         return false;
38089     },
38090
38091     /**
38092      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
38093      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
38094      * @param {Number} index The zero-based index of the list item to select
38095      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
38096      * selected item if it is not currently in view (defaults to true)
38097      */
38098     select : function(index, scrollIntoView){
38099         this.selectedIndex = index;
38100         this.view.select(index);
38101         if(scrollIntoView !== false){
38102             var el = this.view.getNode(index);
38103             if(el){
38104                 this.innerList.scrollChildIntoView(el, false);
38105             }
38106         }
38107     },
38108
38109     // private
38110     selectNext : function(){
38111         var ct = this.store.getCount();
38112         if(ct > 0){
38113             if(this.selectedIndex == -1){
38114                 this.select(0);
38115             }else if(this.selectedIndex < ct-1){
38116                 this.select(this.selectedIndex+1);
38117             }
38118         }
38119     },
38120
38121     // private
38122     selectPrev : function(){
38123         var ct = this.store.getCount();
38124         if(ct > 0){
38125             if(this.selectedIndex == -1){
38126                 this.select(0);
38127             }else if(this.selectedIndex != 0){
38128                 this.select(this.selectedIndex-1);
38129             }
38130         }
38131     },
38132
38133     // private
38134     onKeyUp : function(e){
38135         if(this.editable !== false && !e.isSpecialKey()){
38136             this.lastKey = e.getKey();
38137             this.dqTask.delay(this.queryDelay);
38138         }
38139     },
38140
38141     // private
38142     validateBlur : function(){
38143         return !this.list || !this.list.isVisible();   
38144     },
38145
38146     // private
38147     initQuery : function(){
38148         this.doQuery(this.getRawValue());
38149     },
38150
38151     // private
38152     doForce : function(){
38153         if(this.el.dom.value.length > 0){
38154             this.el.dom.value =
38155                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
38156             this.applyEmptyText();
38157         }
38158     },
38159
38160     /**
38161      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
38162      * query allowing the query action to be canceled if needed.
38163      * @param {String} query The SQL query to execute
38164      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
38165      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
38166      * saved in the current store (defaults to false)
38167      */
38168     doQuery : function(q, forceAll){
38169         if(q === undefined || q === null){
38170             q = '';
38171         }
38172         var qe = {
38173             query: q,
38174             forceAll: forceAll,
38175             combo: this,
38176             cancel:false
38177         };
38178         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
38179             return false;
38180         }
38181         q = qe.query;
38182         forceAll = qe.forceAll;
38183         if(forceAll === true || (q.length >= this.minChars)){
38184             if(this.lastQuery != q || this.alwaysQuery){
38185                 this.lastQuery = q;
38186                 if(this.mode == 'local'){
38187                     this.selectedIndex = -1;
38188                     if(forceAll){
38189                         this.store.clearFilter();
38190                     }else{
38191                         this.store.filter(this.displayField, q);
38192                     }
38193                     this.onLoad();
38194                 }else{
38195                     this.store.baseParams[this.queryParam] = q;
38196                     this.store.load({
38197                         params: this.getParams(q)
38198                     });
38199                     this.expand();
38200                 }
38201             }else{
38202                 this.selectedIndex = -1;
38203                 this.onLoad();   
38204             }
38205         }
38206     },
38207
38208     // private
38209     getParams : function(q){
38210         var p = {};
38211         //p[this.queryParam] = q;
38212         if(this.pageSize){
38213             p.start = 0;
38214             p.limit = this.pageSize;
38215         }
38216         return p;
38217     },
38218
38219     /**
38220      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
38221      */
38222     collapse : function(){
38223         if(!this.isExpanded()){
38224             return;
38225         }
38226         this.list.hide();
38227         Roo.get(document).un('mousedown', this.collapseIf, this);
38228         Roo.get(document).un('mousewheel', this.collapseIf, this);
38229         if (!this.editable) {
38230             Roo.get(document).un('keydown', this.listKeyPress, this);
38231         }
38232         this.fireEvent('collapse', this);
38233     },
38234
38235     // private
38236     collapseIf : function(e){
38237         if(!e.within(this.wrap) && !e.within(this.list)){
38238             this.collapse();
38239         }
38240     },
38241
38242     /**
38243      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
38244      */
38245     expand : function(){
38246         if(this.isExpanded() || !this.hasFocus){
38247             return;
38248         }
38249         this.list.alignTo(this.el, this.listAlign);
38250         this.list.show();
38251         Roo.get(document).on('mousedown', this.collapseIf, this);
38252         Roo.get(document).on('mousewheel', this.collapseIf, this);
38253         if (!this.editable) {
38254             Roo.get(document).on('keydown', this.listKeyPress, this);
38255         }
38256         
38257         this.fireEvent('expand', this);
38258     },
38259
38260     // private
38261     // Implements the default empty TriggerField.onTriggerClick function
38262     onTriggerClick : function(){
38263         if(this.disabled){
38264             return;
38265         }
38266         if(this.isExpanded()){
38267             this.collapse();
38268             if (!this.blockFocus) {
38269                 this.el.focus();
38270             }
38271             
38272         }else {
38273             this.hasFocus = true;
38274             if(this.triggerAction == 'all') {
38275                 this.doQuery(this.allQuery, true);
38276             } else {
38277                 this.doQuery(this.getRawValue());
38278             }
38279             if (!this.blockFocus) {
38280                 this.el.focus();
38281             }
38282         }
38283     },
38284     listKeyPress : function(e)
38285     {
38286         //Roo.log('listkeypress');
38287         // scroll to first matching element based on key pres..
38288         if (e.isSpecialKey()) {
38289             return false;
38290         }
38291         var k = String.fromCharCode(e.getKey()).toUpperCase();
38292         //Roo.log(k);
38293         var match  = false;
38294         var csel = this.view.getSelectedNodes();
38295         var cselitem = false;
38296         if (csel.length) {
38297             var ix = this.view.indexOf(csel[0]);
38298             cselitem  = this.store.getAt(ix);
38299             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
38300                 cselitem = false;
38301             }
38302             
38303         }
38304         
38305         this.store.each(function(v) { 
38306             if (cselitem) {
38307                 // start at existing selection.
38308                 if (cselitem.id == v.id) {
38309                     cselitem = false;
38310                 }
38311                 return;
38312             }
38313                 
38314             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
38315                 match = this.store.indexOf(v);
38316                 return false;
38317             }
38318         }, this);
38319         
38320         if (match === false) {
38321             return true; // no more action?
38322         }
38323         // scroll to?
38324         this.view.select(match);
38325         var sn = Roo.get(this.view.getSelectedNodes()[0])
38326         sn.scrollIntoView(sn.dom.parentNode, false);
38327     }
38328
38329     /** 
38330     * @cfg {Boolean} grow 
38331     * @hide 
38332     */
38333     /** 
38334     * @cfg {Number} growMin 
38335     * @hide 
38336     */
38337     /** 
38338     * @cfg {Number} growMax 
38339     * @hide 
38340     */
38341     /**
38342      * @hide
38343      * @method autoSize
38344      */
38345 });/*
38346  * Based on:
38347  * Ext JS Library 1.1.1
38348  * Copyright(c) 2006-2007, Ext JS, LLC.
38349  *
38350  * Originally Released Under LGPL - original licence link has changed is not relivant.
38351  *
38352  * Fork - LGPL
38353  * <script type="text/javascript">
38354  */
38355 /**
38356  * @class Roo.form.Checkbox
38357  * @extends Roo.form.Field
38358  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
38359  * @constructor
38360  * Creates a new Checkbox
38361  * @param {Object} config Configuration options
38362  */
38363 Roo.form.Checkbox = function(config){
38364     Roo.form.Checkbox.superclass.constructor.call(this, config);
38365     this.addEvents({
38366         /**
38367          * @event check
38368          * Fires when the checkbox is checked or unchecked.
38369              * @param {Roo.form.Checkbox} this This checkbox
38370              * @param {Boolean} checked The new checked value
38371              */
38372         check : true
38373     });
38374 };
38375
38376 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
38377     /**
38378      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
38379      */
38380     focusClass : undefined,
38381     /**
38382      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
38383      */
38384     fieldClass: "x-form-field",
38385     /**
38386      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
38387      */
38388     checked: false,
38389     /**
38390      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38391      * {tag: "input", type: "checkbox", autocomplete: "off"})
38392      */
38393     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
38394     /**
38395      * @cfg {String} boxLabel The text that appears beside the checkbox
38396      */
38397     boxLabel : "",
38398     /**
38399      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
38400      */  
38401     inputValue : '1',
38402     /**
38403      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
38404      */
38405      valueOff: '0', // value when not checked..
38406
38407     actionMode : 'viewEl', 
38408     //
38409     // private
38410     itemCls : 'x-menu-check-item x-form-item',
38411     groupClass : 'x-menu-group-item',
38412     inputType : 'hidden',
38413     
38414     
38415     inSetChecked: false, // check that we are not calling self...
38416     
38417     inputElement: false, // real input element?
38418     basedOn: false, // ????
38419     
38420     isFormField: true, // not sure where this is needed!!!!
38421
38422     onResize : function(){
38423         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
38424         if(!this.boxLabel){
38425             this.el.alignTo(this.wrap, 'c-c');
38426         }
38427     },
38428
38429     initEvents : function(){
38430         Roo.form.Checkbox.superclass.initEvents.call(this);
38431         this.el.on("click", this.onClick,  this);
38432         this.el.on("change", this.onClick,  this);
38433     },
38434
38435
38436     getResizeEl : function(){
38437         return this.wrap;
38438     },
38439
38440     getPositionEl : function(){
38441         return this.wrap;
38442     },
38443
38444     // private
38445     onRender : function(ct, position){
38446         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
38447         /*
38448         if(this.inputValue !== undefined){
38449             this.el.dom.value = this.inputValue;
38450         }
38451         */
38452         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
38453         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
38454         var viewEl = this.wrap.createChild({ 
38455             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
38456         this.viewEl = viewEl;   
38457         this.wrap.on('click', this.onClick,  this); 
38458         
38459         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
38460         this.el.on('propertychange', this.setFromHidden,  this);  //ie
38461         
38462         
38463         
38464         if(this.boxLabel){
38465             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
38466         //    viewEl.on('click', this.onClick,  this); 
38467         }
38468         //if(this.checked){
38469             this.setChecked(this.checked);
38470         //}else{
38471             //this.checked = this.el.dom;
38472         //}
38473
38474     },
38475
38476     // private
38477     initValue : Roo.emptyFn,
38478
38479     /**
38480      * Returns the checked state of the checkbox.
38481      * @return {Boolean} True if checked, else false
38482      */
38483     getValue : function(){
38484         if(this.el){
38485             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
38486         }
38487         return this.valueOff;
38488         
38489     },
38490
38491         // private
38492     onClick : function(){ 
38493         this.setChecked(!this.checked);
38494
38495         //if(this.el.dom.checked != this.checked){
38496         //    this.setValue(this.el.dom.checked);
38497        // }
38498     },
38499
38500     /**
38501      * Sets the checked state of the checkbox.
38502      * On is always based on a string comparison between inputValue and the param.
38503      * @param {Boolean/String} value - the value to set 
38504      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
38505      */
38506     setValue : function(v,suppressEvent){
38507         
38508         
38509         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
38510         //if(this.el && this.el.dom){
38511         //    this.el.dom.checked = this.checked;
38512         //    this.el.dom.defaultChecked = this.checked;
38513         //}
38514         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
38515         //this.fireEvent("check", this, this.checked);
38516     },
38517     // private..
38518     setChecked : function(state,suppressEvent)
38519     {
38520         if (this.inSetChecked) {
38521             this.checked = state;
38522             return;
38523         }
38524         
38525     
38526         if(this.wrap){
38527             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
38528         }
38529         this.checked = state;
38530         if(suppressEvent !== true){
38531             this.fireEvent('check', this, state);
38532         }
38533         this.inSetChecked = true;
38534         this.el.dom.value = state ? this.inputValue : this.valueOff;
38535         this.inSetChecked = false;
38536         
38537     },
38538     // handle setting of hidden value by some other method!!?!?
38539     setFromHidden: function()
38540     {
38541         if(!this.el){
38542             return;
38543         }
38544         //console.log("SET FROM HIDDEN");
38545         //alert('setFrom hidden');
38546         this.setValue(this.el.dom.value);
38547     },
38548     
38549     onDestroy : function()
38550     {
38551         if(this.viewEl){
38552             Roo.get(this.viewEl).remove();
38553         }
38554          
38555         Roo.form.Checkbox.superclass.onDestroy.call(this);
38556     }
38557
38558 });/*
38559  * Based on:
38560  * Ext JS Library 1.1.1
38561  * Copyright(c) 2006-2007, Ext JS, LLC.
38562  *
38563  * Originally Released Under LGPL - original licence link has changed is not relivant.
38564  *
38565  * Fork - LGPL
38566  * <script type="text/javascript">
38567  */
38568  
38569 /**
38570  * @class Roo.form.Radio
38571  * @extends Roo.form.Checkbox
38572  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
38573  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
38574  * @constructor
38575  * Creates a new Radio
38576  * @param {Object} config Configuration options
38577  */
38578 Roo.form.Radio = function(){
38579     Roo.form.Radio.superclass.constructor.apply(this, arguments);
38580 };
38581 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
38582     inputType: 'radio',
38583
38584     /**
38585      * If this radio is part of a group, it will return the selected value
38586      * @return {String}
38587      */
38588     getGroupValue : function(){
38589         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
38590     }
38591 });//<script type="text/javascript">
38592
38593 /*
38594  * Ext JS Library 1.1.1
38595  * Copyright(c) 2006-2007, Ext JS, LLC.
38596  * licensing@extjs.com
38597  * 
38598  * http://www.extjs.com/license
38599  */
38600  
38601  /*
38602   * 
38603   * Known bugs:
38604   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
38605   * - IE ? - no idea how much works there.
38606   * 
38607   * 
38608   * 
38609   */
38610  
38611
38612 /**
38613  * @class Ext.form.HtmlEditor
38614  * @extends Ext.form.Field
38615  * Provides a lightweight HTML Editor component.
38616  * WARNING - THIS CURRENTlY ONLY WORKS ON FIREFOX - USE FCKeditor for a cross platform version
38617  * 
38618  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
38619  * supported by this editor.</b><br/><br/>
38620  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
38621  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
38622  */
38623 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
38624       /**
38625      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
38626      */
38627     toolbars : false,
38628     /**
38629      * @cfg {String} createLinkText The default text for the create link prompt
38630      */
38631     createLinkText : 'Please enter the URL for the link:',
38632     /**
38633      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
38634      */
38635     defaultLinkValue : 'http:/'+'/',
38636    
38637      /**
38638      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
38639      *                        Roo.resizable.
38640      */
38641     resizable : false,
38642      /**
38643      * @cfg {Number} height (in pixels)
38644      */   
38645     height: 300,
38646    /**
38647      * @cfg {Number} width (in pixels)
38648      */   
38649     width: 500,
38650     
38651     /**
38652      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
38653      * 
38654      */
38655     stylesheets: false,
38656     
38657     // id of frame..
38658     frameId: false,
38659     
38660     // private properties
38661     validationEvent : false,
38662     deferHeight: true,
38663     initialized : false,
38664     activated : false,
38665     sourceEditMode : false,
38666     onFocus : Roo.emptyFn,
38667     iframePad:3,
38668     hideMode:'offsets',
38669     
38670     defaultAutoCreate : { // modified by initCompnoent..
38671         tag: "textarea",
38672         style:"width:500px;height:300px;",
38673         autocomplete: "off"
38674     },
38675
38676     // private
38677     initComponent : function(){
38678         this.addEvents({
38679             /**
38680              * @event initialize
38681              * Fires when the editor is fully initialized (including the iframe)
38682              * @param {HtmlEditor} this
38683              */
38684             initialize: true,
38685             /**
38686              * @event activate
38687              * Fires when the editor is first receives the focus. Any insertion must wait
38688              * until after this event.
38689              * @param {HtmlEditor} this
38690              */
38691             activate: true,
38692              /**
38693              * @event beforesync
38694              * Fires before the textarea is updated with content from the editor iframe. Return false
38695              * to cancel the sync.
38696              * @param {HtmlEditor} this
38697              * @param {String} html
38698              */
38699             beforesync: true,
38700              /**
38701              * @event beforepush
38702              * Fires before the iframe editor is updated with content from the textarea. Return false
38703              * to cancel the push.
38704              * @param {HtmlEditor} this
38705              * @param {String} html
38706              */
38707             beforepush: true,
38708              /**
38709              * @event sync
38710              * Fires when the textarea is updated with content from the editor iframe.
38711              * @param {HtmlEditor} this
38712              * @param {String} html
38713              */
38714             sync: true,
38715              /**
38716              * @event push
38717              * Fires when the iframe editor is updated with content from the textarea.
38718              * @param {HtmlEditor} this
38719              * @param {String} html
38720              */
38721             push: true,
38722              /**
38723              * @event editmodechange
38724              * Fires when the editor switches edit modes
38725              * @param {HtmlEditor} this
38726              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
38727              */
38728             editmodechange: true,
38729             /**
38730              * @event editorevent
38731              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
38732              * @param {HtmlEditor} this
38733              */
38734             editorevent: true
38735         });
38736         this.defaultAutoCreate =  {
38737             tag: "textarea",
38738             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
38739             autocomplete: "off"
38740         };
38741     },
38742
38743     /**
38744      * Protected method that will not generally be called directly. It
38745      * is called when the editor creates its toolbar. Override this method if you need to
38746      * add custom toolbar buttons.
38747      * @param {HtmlEditor} editor
38748      */
38749     createToolbar : function(editor){
38750         if (!editor.toolbars || !editor.toolbars.length) {
38751             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
38752         }
38753         
38754         for (var i =0 ; i < editor.toolbars.length;i++) {
38755             editor.toolbars[i] = Roo.factory(editor.toolbars[i], Roo.form.HtmlEditor);
38756             editor.toolbars[i].init(editor);
38757         }
38758          
38759         
38760     },
38761
38762     /**
38763      * Protected method that will not generally be called directly. It
38764      * is called when the editor initializes the iframe with HTML contents. Override this method if you
38765      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
38766      */
38767     getDocMarkup : function(){
38768         // body styles..
38769         var st = '';
38770         if (this.stylesheets === false) {
38771             
38772             Roo.get(document.head).select('style').each(function(node) {
38773                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
38774             });
38775             
38776             Roo.get(document.head).select('link').each(function(node) { 
38777                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
38778             });
38779             
38780         } else if (!this.stylesheets.length) {
38781                 // simple..
38782                 st = '<style type="text/css">' +
38783                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
38784                    '</style>';
38785         } else {
38786             Roo.each(this.stylesheets, function(s) {
38787                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
38788             });
38789             
38790         }
38791         
38792         return '<html><head>' + st  +
38793             //<style type="text/css">' +
38794             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
38795             //'</style>' +
38796             ' </head><body></body></html>';
38797     },
38798
38799     // private
38800     onRender : function(ct, position)
38801     {
38802         var _t = this;
38803         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
38804         this.el.dom.style.border = '0 none';
38805         this.el.dom.setAttribute('tabIndex', -1);
38806         this.el.addClass('x-hidden');
38807         if(Roo.isIE){ // fix IE 1px bogus margin
38808             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
38809         }
38810         this.wrap = this.el.wrap({
38811             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
38812         });
38813         
38814         if (this.resizable) {
38815             this.resizeEl = new Roo.Resizable(this.wrap, {
38816                 pinned : true,
38817                 wrap: true,
38818                 dynamic : true,
38819                 minHeight : this.height,
38820                 height: this.height,
38821                 handles : this.resizable,
38822                 width: this.width,
38823                 listeners : {
38824                     resize : function(r, w, h) {
38825                         _t.onResize(w,h); // -something
38826                     }
38827                 }
38828             });
38829             
38830         }
38831
38832         this.frameId = Roo.id();
38833         
38834         this.createToolbar(this);
38835         
38836       
38837         
38838         var iframe = this.wrap.createChild({
38839             tag: 'iframe',
38840             id: this.frameId,
38841             name: this.frameId,
38842             frameBorder : 'no',
38843             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
38844         }, this.el
38845         );
38846         
38847        // console.log(iframe);
38848         //this.wrap.dom.appendChild(iframe);
38849
38850         this.iframe = iframe.dom;
38851
38852          this.assignDocWin();
38853         
38854         this.doc.designMode = 'on';
38855        
38856         this.doc.open();
38857         this.doc.write(this.getDocMarkup());
38858         this.doc.close();
38859
38860         
38861         var task = { // must defer to wait for browser to be ready
38862             run : function(){
38863                 //console.log("run task?" + this.doc.readyState);
38864                 this.assignDocWin();
38865                 if(this.doc.body || this.doc.readyState == 'complete'){
38866                     try {
38867                         this.doc.designMode="on";
38868                     } catch (e) {
38869                         return;
38870                     }
38871                     Roo.TaskMgr.stop(task);
38872                     this.initEditor.defer(10, this);
38873                 }
38874             },
38875             interval : 10,
38876             duration:10000,
38877             scope: this
38878         };
38879         Roo.TaskMgr.start(task);
38880
38881         if(!this.width){
38882             this.setSize(this.wrap.getSize());
38883         }
38884         if (this.resizeEl) {
38885             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
38886             // should trigger onReize..
38887         }
38888     },
38889
38890     // private
38891     onResize : function(w, h)
38892     {
38893         //Roo.log('resize: ' +w + ',' + h );
38894         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
38895         if(this.el && this.iframe){
38896             if(typeof w == 'number'){
38897                 var aw = w - this.wrap.getFrameWidth('lr');
38898                 this.el.setWidth(this.adjustWidth('textarea', aw));
38899                 this.iframe.style.width = aw + 'px';
38900             }
38901             if(typeof h == 'number'){
38902                 var tbh = 0;
38903                 for (var i =0; i < this.toolbars.length;i++) {
38904                     // fixme - ask toolbars for heights?
38905                     tbh += this.toolbars[i].tb.el.getHeight();
38906                     if (this.toolbars[i].footer) {
38907                         tbh += this.toolbars[i].footer.el.getHeight();
38908                     }
38909                 }
38910                 
38911                 
38912                 
38913                 
38914                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
38915                 ah -= 5; // knock a few pixes off for look..
38916                 this.el.setHeight(this.adjustWidth('textarea', ah));
38917                 this.iframe.style.height = ah + 'px';
38918                 if(this.doc){
38919                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
38920                 }
38921             }
38922         }
38923     },
38924
38925     /**
38926      * Toggles the editor between standard and source edit mode.
38927      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
38928      */
38929     toggleSourceEdit : function(sourceEditMode){
38930         
38931         this.sourceEditMode = sourceEditMode === true;
38932         
38933         if(this.sourceEditMode){
38934           
38935             this.syncValue();
38936             this.iframe.className = 'x-hidden';
38937             this.el.removeClass('x-hidden');
38938             this.el.dom.removeAttribute('tabIndex');
38939             this.el.focus();
38940         }else{
38941              
38942             this.pushValue();
38943             this.iframe.className = '';
38944             this.el.addClass('x-hidden');
38945             this.el.dom.setAttribute('tabIndex', -1);
38946             this.deferFocus();
38947         }
38948         this.setSize(this.wrap.getSize());
38949         this.fireEvent('editmodechange', this, this.sourceEditMode);
38950     },
38951
38952     // private used internally
38953     createLink : function(){
38954         var url = prompt(this.createLinkText, this.defaultLinkValue);
38955         if(url && url != 'http:/'+'/'){
38956             this.relayCmd('createlink', url);
38957         }
38958     },
38959
38960     // private (for BoxComponent)
38961     adjustSize : Roo.BoxComponent.prototype.adjustSize,
38962
38963     // private (for BoxComponent)
38964     getResizeEl : function(){
38965         return this.wrap;
38966     },
38967
38968     // private (for BoxComponent)
38969     getPositionEl : function(){
38970         return this.wrap;
38971     },
38972
38973     // private
38974     initEvents : function(){
38975         this.originalValue = this.getValue();
38976     },
38977
38978     /**
38979      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
38980      * @method
38981      */
38982     markInvalid : Roo.emptyFn,
38983     /**
38984      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
38985      * @method
38986      */
38987     clearInvalid : Roo.emptyFn,
38988
38989     setValue : function(v){
38990         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
38991         this.pushValue();
38992     },
38993
38994     /**
38995      * Protected method that will not generally be called directly. If you need/want
38996      * custom HTML cleanup, this is the method you should override.
38997      * @param {String} html The HTML to be cleaned
38998      * return {String} The cleaned HTML
38999      */
39000     cleanHtml : function(html){
39001         html = String(html);
39002         if(html.length > 5){
39003             if(Roo.isSafari){ // strip safari nonsense
39004                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
39005             }
39006         }
39007         if(html == '&nbsp;'){
39008             html = '';
39009         }
39010         return html;
39011     },
39012
39013     /**
39014      * Protected method that will not generally be called directly. Syncs the contents
39015      * of the editor iframe with the textarea.
39016      */
39017     syncValue : function(){
39018         if(this.initialized){
39019             var bd = (this.doc.body || this.doc.documentElement);
39020             this.cleanUpPaste();
39021             var html = bd.innerHTML;
39022             if(Roo.isSafari){
39023                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
39024                 var m = bs.match(/text-align:(.*?);/i);
39025                 if(m && m[1]){
39026                     html = '<div style="'+m[0]+'">' + html + '</div>';
39027                 }
39028             }
39029             html = this.cleanHtml(html);
39030             if(this.fireEvent('beforesync', this, html) !== false){
39031                 this.el.dom.value = html;
39032                 this.fireEvent('sync', this, html);
39033             }
39034         }
39035     },
39036
39037     /**
39038      * Protected method that will not generally be called directly. Pushes the value of the textarea
39039      * into the iframe editor.
39040      */
39041     pushValue : function(){
39042         if(this.initialized){
39043             var v = this.el.dom.value;
39044             if(v.length < 1){
39045                 v = '&#160;';
39046             }
39047             
39048             if(this.fireEvent('beforepush', this, v) !== false){
39049                 var d = (this.doc.body || this.doc.documentElement);
39050                 d.innerHTML = v;
39051                 this.cleanUpPaste();
39052                 this.el.dom.value = d.innerHTML;
39053                 this.fireEvent('push', this, v);
39054             }
39055         }
39056     },
39057
39058     // private
39059     deferFocus : function(){
39060         this.focus.defer(10, this);
39061     },
39062
39063     // doc'ed in Field
39064     focus : function(){
39065         if(this.win && !this.sourceEditMode){
39066             this.win.focus();
39067         }else{
39068             this.el.focus();
39069         }
39070     },
39071     
39072     assignDocWin: function()
39073     {
39074         var iframe = this.iframe;
39075         
39076          if(Roo.isIE){
39077             this.doc = iframe.contentWindow.document;
39078             this.win = iframe.contentWindow;
39079         } else {
39080             if (!Roo.get(this.frameId)) {
39081                 return;
39082             }
39083             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
39084             this.win = Roo.get(this.frameId).dom.contentWindow;
39085         }
39086     },
39087     
39088     // private
39089     initEditor : function(){
39090         //console.log("INIT EDITOR");
39091         this.assignDocWin();
39092         
39093         
39094         
39095         this.doc.designMode="on";
39096         this.doc.open();
39097         this.doc.write(this.getDocMarkup());
39098         this.doc.close();
39099         
39100         var dbody = (this.doc.body || this.doc.documentElement);
39101         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
39102         // this copies styles from the containing element into thsi one..
39103         // not sure why we need all of this..
39104         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
39105         ss['background-attachment'] = 'fixed'; // w3c
39106         dbody.bgProperties = 'fixed'; // ie
39107         Roo.DomHelper.applyStyles(dbody, ss);
39108         Roo.EventManager.on(this.doc, {
39109             //'mousedown': this.onEditorEvent,
39110             'mouseup': this.onEditorEvent,
39111             'dblclick': this.onEditorEvent,
39112             'click': this.onEditorEvent,
39113             'keyup': this.onEditorEvent,
39114             buffer:100,
39115             scope: this
39116         });
39117         if(Roo.isGecko){
39118             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
39119         }
39120         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
39121             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
39122         }
39123         this.initialized = true;
39124
39125         this.fireEvent('initialize', this);
39126         this.pushValue();
39127     },
39128
39129     // private
39130     onDestroy : function(){
39131         
39132         
39133         
39134         if(this.rendered){
39135             
39136             for (var i =0; i < this.toolbars.length;i++) {
39137                 // fixme - ask toolbars for heights?
39138                 this.toolbars[i].onDestroy();
39139             }
39140             
39141             this.wrap.dom.innerHTML = '';
39142             this.wrap.remove();
39143         }
39144     },
39145
39146     // private
39147     onFirstFocus : function(){
39148         
39149         this.assignDocWin();
39150         
39151         
39152         this.activated = true;
39153         for (var i =0; i < this.toolbars.length;i++) {
39154             this.toolbars[i].onFirstFocus();
39155         }
39156        
39157         if(Roo.isGecko){ // prevent silly gecko errors
39158             this.win.focus();
39159             var s = this.win.getSelection();
39160             if(!s.focusNode || s.focusNode.nodeType != 3){
39161                 var r = s.getRangeAt(0);
39162                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
39163                 r.collapse(true);
39164                 this.deferFocus();
39165             }
39166             try{
39167                 this.execCmd('useCSS', true);
39168                 this.execCmd('styleWithCSS', false);
39169             }catch(e){}
39170         }
39171         this.fireEvent('activate', this);
39172     },
39173
39174     // private
39175     adjustFont: function(btn){
39176         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
39177         //if(Roo.isSafari){ // safari
39178         //    adjust *= 2;
39179        // }
39180         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
39181         if(Roo.isSafari){ // safari
39182             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
39183             v =  (v < 10) ? 10 : v;
39184             v =  (v > 48) ? 48 : v;
39185             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
39186             
39187         }
39188         
39189         
39190         v = Math.max(1, v+adjust);
39191         
39192         this.execCmd('FontSize', v  );
39193     },
39194
39195     onEditorEvent : function(e){
39196         this.fireEvent('editorevent', this, e);
39197       //  this.updateToolbar();
39198         this.syncValue();
39199     },
39200
39201     insertTag : function(tg)
39202     {
39203         // could be a bit smarter... -> wrap the current selected tRoo..
39204         
39205         this.execCmd("formatblock",   tg);
39206         
39207     },
39208     
39209     insertText : function(txt)
39210     {
39211         
39212         
39213         range = this.createRange();
39214         range.deleteContents();
39215                //alert(Sender.getAttribute('label'));
39216                
39217         range.insertNode(this.doc.createTextNode(txt));
39218     } ,
39219     
39220     // private
39221     relayBtnCmd : function(btn){
39222         this.relayCmd(btn.cmd);
39223     },
39224
39225     /**
39226      * Executes a Midas editor command on the editor document and performs necessary focus and
39227      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
39228      * @param {String} cmd The Midas command
39229      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
39230      */
39231     relayCmd : function(cmd, value){
39232         this.win.focus();
39233         this.execCmd(cmd, value);
39234         this.fireEvent('editorevent', this);
39235         //this.updateToolbar();
39236         this.deferFocus();
39237     },
39238
39239     /**
39240      * Executes a Midas editor command directly on the editor document.
39241      * For visual commands, you should use {@link #relayCmd} instead.
39242      * <b>This should only be called after the editor is initialized.</b>
39243      * @param {String} cmd The Midas command
39244      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
39245      */
39246     execCmd : function(cmd, value){
39247         this.doc.execCommand(cmd, false, value === undefined ? null : value);
39248         this.syncValue();
39249     },
39250
39251    
39252     /**
39253      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
39254      * to insert tRoo.
39255      * @param {String} text
39256      */
39257     insertAtCursor : function(text){
39258         if(!this.activated){
39259             return;
39260         }
39261         if(Roo.isIE){
39262             this.win.focus();
39263             var r = this.doc.selection.createRange();
39264             if(r){
39265                 r.collapse(true);
39266                 r.pasteHTML(text);
39267                 this.syncValue();
39268                 this.deferFocus();
39269             }
39270         }else if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
39271             this.win.focus();
39272             this.execCmd('InsertHTML', text);
39273             this.deferFocus();
39274         }
39275     },
39276  // private
39277     mozKeyPress : function(e){
39278         if(e.ctrlKey){
39279             var c = e.getCharCode(), cmd;
39280           
39281             if(c > 0){
39282                 c = String.fromCharCode(c).toLowerCase();
39283                 switch(c){
39284                     case 'b':
39285                         cmd = 'bold';
39286                     break;
39287                     case 'i':
39288                         cmd = 'italic';
39289                     break;
39290                     case 'u':
39291                         cmd = 'underline';
39292                     case 'v':
39293                         this.cleanUpPaste.defer(100, this);
39294                         return;
39295                     break;
39296                 }
39297                 if(cmd){
39298                     this.win.focus();
39299                     this.execCmd(cmd);
39300                     this.deferFocus();
39301                     e.preventDefault();
39302                 }
39303                 
39304             }
39305         }
39306     },
39307
39308     // private
39309     fixKeys : function(){ // load time branching for fastest keydown performance
39310         if(Roo.isIE){
39311             return function(e){
39312                 var k = e.getKey(), r;
39313                 if(k == e.TAB){
39314                     e.stopEvent();
39315                     r = this.doc.selection.createRange();
39316                     if(r){
39317                         r.collapse(true);
39318                         r.pasteHTML('&#160;&#160;&#160;&#160;');
39319                         this.deferFocus();
39320                     }
39321                     return;
39322                 }
39323                 
39324                 if(k == e.ENTER){
39325                     r = this.doc.selection.createRange();
39326                     if(r){
39327                         var target = r.parentElement();
39328                         if(!target || target.tagName.toLowerCase() != 'li'){
39329                             e.stopEvent();
39330                             r.pasteHTML('<br />');
39331                             r.collapse(false);
39332                             r.select();
39333                         }
39334                     }
39335                 }
39336                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
39337                     this.cleanUpPaste.defer(100, this);
39338                     return;
39339                 }
39340                 
39341                 
39342             };
39343         }else if(Roo.isOpera){
39344             return function(e){
39345                 var k = e.getKey();
39346                 if(k == e.TAB){
39347                     e.stopEvent();
39348                     this.win.focus();
39349                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
39350                     this.deferFocus();
39351                 }
39352                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
39353                     this.cleanUpPaste.defer(100, this);
39354                     return;
39355                 }
39356                 
39357             };
39358         }else if(Roo.isSafari){
39359             return function(e){
39360                 var k = e.getKey();
39361                 
39362                 if(k == e.TAB){
39363                     e.stopEvent();
39364                     this.execCmd('InsertText','\t');
39365                     this.deferFocus();
39366                     return;
39367                 }
39368                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
39369                     this.cleanUpPaste.defer(100, this);
39370                     return;
39371                 }
39372                 
39373              };
39374         }
39375     }(),
39376     
39377     getAllAncestors: function()
39378     {
39379         var p = this.getSelectedNode();
39380         var a = [];
39381         if (!p) {
39382             a.push(p); // push blank onto stack..
39383             p = this.getParentElement();
39384         }
39385         
39386         
39387         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
39388             a.push(p);
39389             p = p.parentNode;
39390         }
39391         a.push(this.doc.body);
39392         return a;
39393     },
39394     lastSel : false,
39395     lastSelNode : false,
39396     
39397     
39398     getSelection : function() 
39399     {
39400         this.assignDocWin();
39401         return Roo.isIE ? this.doc.selection : this.win.getSelection();
39402     },
39403     
39404     getSelectedNode: function() 
39405     {
39406         // this may only work on Gecko!!!
39407         
39408         // should we cache this!!!!
39409         
39410         
39411         
39412          
39413         var range = this.createRange(this.getSelection()).cloneRange();
39414         
39415         if (Roo.isIE) {
39416             var parent = range.parentElement();
39417             while (true) {
39418                 var testRange = range.duplicate();
39419                 testRange.moveToElementText(parent);
39420                 if (testRange.inRange(range)) {
39421                     break;
39422                 }
39423                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
39424                     break;
39425                 }
39426                 parent = parent.parentElement;
39427             }
39428             return parent;
39429         }
39430         
39431         // is ancestor a text element.
39432         var ac =  range.commonAncestorContainer;
39433         if (ac.nodeType == 3) {
39434             ac = ac.parentNode;
39435         }
39436         
39437         var ar = ac.childNodes;
39438          
39439         var nodes = [];
39440         var other_nodes = [];
39441         var has_other_nodes = false;
39442         for (var i=0;i<ar.length;i++) {
39443             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
39444                 continue;
39445             }
39446             // fullly contained node.
39447             
39448             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
39449                 nodes.push(ar[i]);
39450                 continue;
39451             }
39452             
39453             // probably selected..
39454             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
39455                 other_nodes.push(ar[i]);
39456                 continue;
39457             }
39458             // outer..
39459             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
39460                 continue;
39461             }
39462             
39463             
39464             has_other_nodes = true;
39465         }
39466         if (!nodes.length && other_nodes.length) {
39467             nodes= other_nodes;
39468         }
39469         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
39470             return false;
39471         }
39472         
39473         return nodes[0];
39474     },
39475     createRange: function(sel)
39476     {
39477         // this has strange effects when using with 
39478         // top toolbar - not sure if it's a great idea.
39479         //this.editor.contentWindow.focus();
39480         if (typeof sel != "undefined") {
39481             try {
39482                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
39483             } catch(e) {
39484                 return this.doc.createRange();
39485             }
39486         } else {
39487             return this.doc.createRange();
39488         }
39489     },
39490     getParentElement: function()
39491     {
39492         
39493         this.assignDocWin();
39494         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
39495         
39496         var range = this.createRange(sel);
39497          
39498         try {
39499             var p = range.commonAncestorContainer;
39500             while (p.nodeType == 3) { // text node
39501                 p = p.parentNode;
39502             }
39503             return p;
39504         } catch (e) {
39505             return null;
39506         }
39507     
39508     },
39509     /***
39510      *
39511      * Range intersection.. the hard stuff...
39512      *  '-1' = before
39513      *  '0' = hits..
39514      *  '1' = after.
39515      *         [ -- selected range --- ]
39516      *   [fail]                        [fail]
39517      *
39518      *    basically..
39519      *      if end is before start or  hits it. fail.
39520      *      if start is after end or hits it fail.
39521      *
39522      *   if either hits (but other is outside. - then it's not 
39523      *   
39524      *    
39525      **/
39526     
39527     
39528     // @see http://www.thismuchiknow.co.uk/?p=64.
39529     rangeIntersectsNode : function(range, node)
39530     {
39531         var nodeRange = node.ownerDocument.createRange();
39532         try {
39533             nodeRange.selectNode(node);
39534         } catch (e) {
39535             nodeRange.selectNodeContents(node);
39536         }
39537     
39538         var rangeStartRange = range.cloneRange();
39539         rangeStartRange.collapse(true);
39540     
39541         var rangeEndRange = range.cloneRange();
39542         rangeEndRange.collapse(false);
39543     
39544         var nodeStartRange = nodeRange.cloneRange();
39545         nodeStartRange.collapse(true);
39546     
39547         var nodeEndRange = nodeRange.cloneRange();
39548         nodeEndRange.collapse(false);
39549     
39550         return rangeStartRange.compareBoundaryPoints(
39551                  Range.START_TO_START, nodeEndRange) == -1 &&
39552                rangeEndRange.compareBoundaryPoints(
39553                  Range.START_TO_START, nodeStartRange) == 1;
39554         
39555          
39556     },
39557     rangeCompareNode : function(range, node)
39558     {
39559         var nodeRange = node.ownerDocument.createRange();
39560         try {
39561             nodeRange.selectNode(node);
39562         } catch (e) {
39563             nodeRange.selectNodeContents(node);
39564         }
39565         
39566         
39567         range.collapse(true);
39568     
39569         nodeRange.collapse(true);
39570      
39571         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
39572         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
39573          
39574         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
39575         
39576         var nodeIsBefore   =  ss == 1;
39577         var nodeIsAfter    = ee == -1;
39578         
39579         if (nodeIsBefore && nodeIsAfter)
39580             return 0; // outer
39581         if (!nodeIsBefore && nodeIsAfter)
39582             return 1; //right trailed.
39583         
39584         if (nodeIsBefore && !nodeIsAfter)
39585             return 2;  // left trailed.
39586         // fully contined.
39587         return 3;
39588     },
39589
39590     // private? - in a new class?
39591     cleanUpPaste :  function()
39592     {
39593         // cleans up the whole document..
39594       //  console.log('cleanuppaste');
39595         this.cleanUpChildren(this.doc.body);
39596         
39597         
39598     },
39599     cleanUpChildren : function (n)
39600     {
39601         if (!n.childNodes.length) {
39602             return;
39603         }
39604         for (var i = n.childNodes.length-1; i > -1 ; i--) {
39605            this.cleanUpChild(n.childNodes[i]);
39606         }
39607     },
39608     
39609     
39610         
39611     
39612     cleanUpChild : function (node)
39613     {
39614         //console.log(node);
39615         if (node.nodeName == "#text") {
39616             // clean up silly Windows -- stuff?
39617             return; 
39618         }
39619         if (node.nodeName == "#comment") {
39620             node.parentNode.removeChild(node);
39621             // clean up silly Windows -- stuff?
39622             return; 
39623         }
39624         
39625         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
39626             // remove node.
39627             node.parentNode.removeChild(node);
39628             return;
39629             
39630         }
39631         
39632         var remove_keep_children= Roo.form.HtmlEditor.remove.indexOf(node.tagName.toLowerCase()) > -1;
39633         
39634         // remove <a name=....> as rendering on yahoo mailer is bored with this.
39635         
39636         if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
39637             remove_keep_children = true;
39638         }
39639         
39640         if (remove_keep_children) {
39641             this.cleanUpChildren(node);
39642             // inserts everything just before this node...
39643             while (node.childNodes.length) {
39644                 var cn = node.childNodes[0];
39645                 node.removeChild(cn);
39646                 node.parentNode.insertBefore(cn, node);
39647             }
39648             node.parentNode.removeChild(node);
39649             return;
39650         }
39651         
39652         if (!node.attributes || !node.attributes.length) {
39653             this.cleanUpChildren(node);
39654             return;
39655         }
39656         
39657         function cleanAttr(n,v)
39658         {
39659             
39660             if (v.match(/^\./) || v.match(/^\//)) {
39661                 return;
39662             }
39663             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
39664                 return;
39665             }
39666             Roo.log("(REMOVE)"+ node.tagName +'.' + n + '=' + v);
39667             node.removeAttribute(n);
39668             
39669         }
39670         
39671         function cleanStyle(n,v)
39672         {
39673             if (v.match(/expression/)) { //XSS?? should we even bother..
39674                 node.removeAttribute(n);
39675                 return;
39676             }
39677             
39678             
39679             var parts = v.split(/;/);
39680             Roo.each(parts, function(p) {
39681                 p = p.replace(/\s+/g,'');
39682                 if (!p.length) {
39683                     return true;
39684                 }
39685                 var l = p.split(':').shift().replace(/\s+/g,'');
39686                 
39687                 // only allow 'c whitelisted system attributes'
39688                 if (Roo.form.HtmlEditor.cwhite.indexOf(l) < 0) {
39689                     Roo.log('(REMOVE)' + node.tagName +'.' + n + ':'+l + '=' + v);
39690                     node.removeAttribute(n);
39691                     return false;
39692                 }
39693                 return true;
39694             });
39695             
39696             
39697         }
39698         
39699         
39700         for (var i = node.attributes.length-1; i > -1 ; i--) {
39701             var a = node.attributes[i];
39702             //console.log(a);
39703             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
39704                 node.removeAttribute(a.name);
39705                 return;
39706             }
39707             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
39708                 cleanAttr(a.name,a.value); // fixme..
39709                 return;
39710             }
39711             if (a.name == 'style') {
39712                 cleanStyle(a.name,a.value);
39713             }
39714             /// clean up MS crap..
39715             if (a.name == 'class') {
39716                 if (a.value.match(/^Mso/)) {
39717                     node.className = '';
39718                 }
39719             }
39720             
39721             // style cleanup!?
39722             // class cleanup?
39723             
39724         }
39725         
39726         
39727         this.cleanUpChildren(node);
39728         
39729         
39730     }
39731     
39732     
39733     // hide stuff that is not compatible
39734     /**
39735      * @event blur
39736      * @hide
39737      */
39738     /**
39739      * @event change
39740      * @hide
39741      */
39742     /**
39743      * @event focus
39744      * @hide
39745      */
39746     /**
39747      * @event specialkey
39748      * @hide
39749      */
39750     /**
39751      * @cfg {String} fieldClass @hide
39752      */
39753     /**
39754      * @cfg {String} focusClass @hide
39755      */
39756     /**
39757      * @cfg {String} autoCreate @hide
39758      */
39759     /**
39760      * @cfg {String} inputType @hide
39761      */
39762     /**
39763      * @cfg {String} invalidClass @hide
39764      */
39765     /**
39766      * @cfg {String} invalidText @hide
39767      */
39768     /**
39769      * @cfg {String} msgFx @hide
39770      */
39771     /**
39772      * @cfg {String} validateOnBlur @hide
39773      */
39774 });
39775
39776 Roo.form.HtmlEditor.white = [
39777         'area', 'br', 'img', 'input', 'hr', 'wbr',
39778         
39779        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
39780        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
39781        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
39782        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
39783        'table',   'ul',         'xmp', 
39784        
39785        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
39786       'thead',   'tr', 
39787      
39788       'dir', 'menu', 'ol', 'ul', 'dl',
39789        
39790       'embed',  'object'
39791 ];
39792
39793
39794 Roo.form.HtmlEditor.black = [
39795     //    'embed',  'object', // enable - backend responsiblity to clean thiese
39796         'applet', // 
39797         'base',   'basefont', 'bgsound', 'blink',  'body', 
39798         'frame',  'frameset', 'head',    'html',   'ilayer', 
39799         'iframe', 'layer',  'link',     'meta',    'object',   
39800         'script', 'style' ,'title',  'xml' // clean later..
39801 ];
39802 Roo.form.HtmlEditor.clean = [
39803     'script', 'style', 'title', 'xml'
39804 ];
39805 Roo.form.HtmlEditor.remove = [
39806     'font'
39807 ];
39808 // attributes..
39809
39810 Roo.form.HtmlEditor.ablack = [
39811     'on'
39812 ];
39813     
39814 Roo.form.HtmlEditor.aclean = [ 
39815     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
39816 ];
39817
39818 // protocols..
39819 Roo.form.HtmlEditor.pwhite= [
39820         'http',  'https',  'mailto'
39821 ];
39822
39823 // white listed style attributes.
39824 Roo.form.HtmlEditor.cwhite= [
39825         'text-align',
39826         'font-size'
39827 ];
39828
39829 // <script type="text/javascript">
39830 /*
39831  * Based on
39832  * Ext JS Library 1.1.1
39833  * Copyright(c) 2006-2007, Ext JS, LLC.
39834  *  
39835  
39836  */
39837
39838 /**
39839  * @class Roo.form.HtmlEditorToolbar1
39840  * Basic Toolbar
39841  * 
39842  * Usage:
39843  *
39844  new Roo.form.HtmlEditor({
39845     ....
39846     toolbars : [
39847         new Roo.form.HtmlEditorToolbar1({
39848             disable : { fonts: 1 , format: 1, ..., ... , ...],
39849             btns : [ .... ]
39850         })
39851     }
39852      
39853  * 
39854  * @cfg {Object} disable List of elements to disable..
39855  * @cfg {Array} btns List of additional buttons.
39856  * 
39857  * 
39858  * NEEDS Extra CSS? 
39859  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
39860  */
39861  
39862 Roo.form.HtmlEditor.ToolbarStandard = function(config)
39863 {
39864     
39865     Roo.apply(this, config);
39866     
39867     // default disabled, based on 'good practice'..
39868     this.disable = this.disable || {};
39869     Roo.applyIf(this.disable, {
39870         fontSize : true,
39871         colors : true,
39872         specialElements : true
39873     });
39874     
39875     
39876     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
39877     // dont call parent... till later.
39878 }
39879
39880 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
39881     
39882     tb: false,
39883     
39884     rendered: false,
39885     
39886     editor : false,
39887     /**
39888      * @cfg {Object} disable  List of toolbar elements to disable
39889          
39890      */
39891     disable : false,
39892       /**
39893      * @cfg {Array} fontFamilies An array of available font families
39894      */
39895     fontFamilies : [
39896         'Arial',
39897         'Courier New',
39898         'Tahoma',
39899         'Times New Roman',
39900         'Verdana'
39901     ],
39902     
39903     specialChars : [
39904            "&#169;",
39905           "&#174;",     
39906           "&#8482;",    
39907           "&#163;" ,    
39908          // "&#8212;",    
39909           "&#8230;",    
39910           "&#247;" ,    
39911         //  "&#225;" ,     ?? a acute?
39912            "&#8364;"    , //Euro
39913        //   "&#8220;"    ,
39914         //  "&#8221;"    ,
39915         //  "&#8226;"    ,
39916           "&#176;"  //   , // degrees
39917
39918          // "&#233;"     , // e ecute
39919          // "&#250;"     , // u ecute?
39920     ],
39921     
39922     specialElements : [
39923         {
39924             text: "Insert Table",
39925             xtype: 'MenuItem',
39926             xns : Roo.Menu,
39927             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
39928                 
39929         },
39930         {    
39931             text: "Insert Image",
39932             xtype: 'MenuItem',
39933             xns : Roo.Menu,
39934             ihtml : '<img src="about:blank"/>'
39935             
39936         }
39937         
39938          
39939     ],
39940     
39941     
39942     inputElements : [ 
39943             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
39944             "input:submit", "input:button", "select", "textarea", "label" ],
39945     formats : [
39946         ["p"] ,  
39947         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
39948         ["pre"],[ "code"], 
39949         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"]
39950     ],
39951      /**
39952      * @cfg {String} defaultFont default font to use.
39953      */
39954     defaultFont: 'tahoma',
39955    
39956     fontSelect : false,
39957     
39958     
39959     formatCombo : false,
39960     
39961     init : function(editor)
39962     {
39963         this.editor = editor;
39964         
39965         
39966         var fid = editor.frameId;
39967         var etb = this;
39968         function btn(id, toggle, handler){
39969             var xid = fid + '-'+ id ;
39970             return {
39971                 id : xid,
39972                 cmd : id,
39973                 cls : 'x-btn-icon x-edit-'+id,
39974                 enableToggle:toggle !== false,
39975                 scope: editor, // was editor...
39976                 handler:handler||editor.relayBtnCmd,
39977                 clickEvent:'mousedown',
39978                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
39979                 tabIndex:-1
39980             };
39981         }
39982         
39983         
39984         
39985         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
39986         this.tb = tb;
39987          // stop form submits
39988         tb.el.on('click', function(e){
39989             e.preventDefault(); // what does this do?
39990         });
39991
39992         if(!this.disable.font && !Roo.isSafari){
39993             /* why no safari for fonts
39994             editor.fontSelect = tb.el.createChild({
39995                 tag:'select',
39996                 tabIndex: -1,
39997                 cls:'x-font-select',
39998                 html: editor.createFontOptions()
39999             });
40000             editor.fontSelect.on('change', function(){
40001                 var font = editor.fontSelect.dom.value;
40002                 editor.relayCmd('fontname', font);
40003                 editor.deferFocus();
40004             }, editor);
40005             tb.add(
40006                 editor.fontSelect.dom,
40007                 '-'
40008             );
40009             */
40010         };
40011         if(!this.disable.formats){
40012             this.formatCombo = new Roo.form.ComboBox({
40013                 store: new Roo.data.SimpleStore({
40014                     id : 'tag',
40015                     fields: ['tag'],
40016                     data : this.formats // from states.js
40017                 }),
40018                 blockFocus : true,
40019                 //autoCreate : {tag: "div",  size: "20"},
40020                 displayField:'tag',
40021                 typeAhead: false,
40022                 mode: 'local',
40023                 editable : false,
40024                 triggerAction: 'all',
40025                 emptyText:'Add tag',
40026                 selectOnFocus:true,
40027                 width:135,
40028                 listeners : {
40029                     'select': function(c, r, i) {
40030                         editor.insertTag(r.get('tag'));
40031                         editor.focus();
40032                     }
40033                 }
40034
40035             });
40036             tb.addField(this.formatCombo);
40037             
40038         }
40039         
40040         if(!this.disable.format){
40041             tb.add(
40042                 btn('bold'),
40043                 btn('italic'),
40044                 btn('underline')
40045             );
40046         };
40047         if(!this.disable.fontSize){
40048             tb.add(
40049                 '-',
40050                 
40051                 
40052                 btn('increasefontsize', false, editor.adjustFont),
40053                 btn('decreasefontsize', false, editor.adjustFont)
40054             );
40055         };
40056         
40057         
40058         if(!this.disable.colors){
40059             tb.add(
40060                 '-', {
40061                     id:editor.frameId +'-forecolor',
40062                     cls:'x-btn-icon x-edit-forecolor',
40063                     clickEvent:'mousedown',
40064                     tooltip: this.buttonTips['forecolor'] || undefined,
40065                     tabIndex:-1,
40066                     menu : new Roo.menu.ColorMenu({
40067                         allowReselect: true,
40068                         focus: Roo.emptyFn,
40069                         value:'000000',
40070                         plain:true,
40071                         selectHandler: function(cp, color){
40072                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
40073                             editor.deferFocus();
40074                         },
40075                         scope: editor,
40076                         clickEvent:'mousedown'
40077                     })
40078                 }, {
40079                     id:editor.frameId +'backcolor',
40080                     cls:'x-btn-icon x-edit-backcolor',
40081                     clickEvent:'mousedown',
40082                     tooltip: this.buttonTips['backcolor'] || undefined,
40083                     tabIndex:-1,
40084                     menu : new Roo.menu.ColorMenu({
40085                         focus: Roo.emptyFn,
40086                         value:'FFFFFF',
40087                         plain:true,
40088                         allowReselect: true,
40089                         selectHandler: function(cp, color){
40090                             if(Roo.isGecko){
40091                                 editor.execCmd('useCSS', false);
40092                                 editor.execCmd('hilitecolor', color);
40093                                 editor.execCmd('useCSS', true);
40094                                 editor.deferFocus();
40095                             }else{
40096                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
40097                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
40098                                 editor.deferFocus();
40099                             }
40100                         },
40101                         scope:editor,
40102                         clickEvent:'mousedown'
40103                     })
40104                 }
40105             );
40106         };
40107         // now add all the items...
40108         
40109
40110         if(!this.disable.alignments){
40111             tb.add(
40112                 '-',
40113                 btn('justifyleft'),
40114                 btn('justifycenter'),
40115                 btn('justifyright')
40116             );
40117         };
40118
40119         //if(!Roo.isSafari){
40120             if(!this.disable.links){
40121                 tb.add(
40122                     '-',
40123                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
40124                 );
40125             };
40126
40127             if(!this.disable.lists){
40128                 tb.add(
40129                     '-',
40130                     btn('insertorderedlist'),
40131                     btn('insertunorderedlist')
40132                 );
40133             }
40134             if(!this.disable.sourceEdit){
40135                 tb.add(
40136                     '-',
40137                     btn('sourceedit', true, function(btn){
40138                         this.toggleSourceEdit(btn.pressed);
40139                     })
40140                 );
40141             }
40142         //}
40143         
40144         var smenu = { };
40145         // special menu.. - needs to be tidied up..
40146         if (!this.disable.special) {
40147             smenu = {
40148                 text: "&#169;",
40149                 cls: 'x-edit-none',
40150                 
40151                 menu : {
40152                     items : []
40153                 }
40154             };
40155             for (var i =0; i < this.specialChars.length; i++) {
40156                 smenu.menu.items.push({
40157                     
40158                     html: this.specialChars[i],
40159                     handler: function(a,b) {
40160                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
40161                         
40162                     },
40163                     tabIndex:-1
40164                 });
40165             }
40166             
40167             
40168             tb.add(smenu);
40169             
40170             
40171         }
40172          
40173         if (!this.disable.specialElements) {
40174             var semenu = {
40175                 text: "Other;",
40176                 cls: 'x-edit-none',
40177                 menu : {
40178                     items : []
40179                 }
40180             };
40181             for (var i =0; i < this.specialElements.length; i++) {
40182                 semenu.menu.items.push(
40183                     Roo.apply({ 
40184                         handler: function(a,b) {
40185                             editor.insertAtCursor(this.ihtml);
40186                         }
40187                     }, this.specialElements[i])
40188                 );
40189                     
40190             }
40191             
40192             tb.add(semenu);
40193             
40194             
40195         }
40196          
40197         
40198         if (this.btns) {
40199             for(var i =0; i< this.btns.length;i++) {
40200                 var b = this.btns[i];
40201                 b.cls =  'x-edit-none';
40202                 b.scope = editor;
40203                 tb.add(b);
40204             }
40205         
40206         }
40207         
40208         
40209         
40210         // disable everything...
40211         
40212         this.tb.items.each(function(item){
40213            if(item.id != editor.frameId+ '-sourceedit'){
40214                 item.disable();
40215             }
40216         });
40217         this.rendered = true;
40218         
40219         // the all the btns;
40220         editor.on('editorevent', this.updateToolbar, this);
40221         // other toolbars need to implement this..
40222         //editor.on('editmodechange', this.updateToolbar, this);
40223     },
40224     
40225     
40226     
40227     /**
40228      * Protected method that will not generally be called directly. It triggers
40229      * a toolbar update by reading the markup state of the current selection in the editor.
40230      */
40231     updateToolbar: function(){
40232
40233         if(!this.editor.activated){
40234             this.editor.onFirstFocus();
40235             return;
40236         }
40237
40238         var btns = this.tb.items.map, 
40239             doc = this.editor.doc,
40240             frameId = this.editor.frameId;
40241
40242         if(!this.disable.font && !Roo.isSafari){
40243             /*
40244             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
40245             if(name != this.fontSelect.dom.value){
40246                 this.fontSelect.dom.value = name;
40247             }
40248             */
40249         }
40250         if(!this.disable.format){
40251             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
40252             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
40253             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
40254         }
40255         if(!this.disable.alignments){
40256             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
40257             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
40258             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
40259         }
40260         if(!Roo.isSafari && !this.disable.lists){
40261             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
40262             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
40263         }
40264         
40265         var ans = this.editor.getAllAncestors();
40266         if (this.formatCombo) {
40267             
40268             
40269             var store = this.formatCombo.store;
40270             this.formatCombo.setValue("");
40271             for (var i =0; i < ans.length;i++) {
40272                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
40273                     // select it..
40274                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
40275                     break;
40276                 }
40277             }
40278         }
40279         
40280         
40281         
40282         // hides menus... - so this cant be on a menu...
40283         Roo.menu.MenuMgr.hideAll();
40284
40285         //this.editorsyncValue();
40286     },
40287    
40288     
40289     createFontOptions : function(){
40290         var buf = [], fs = this.fontFamilies, ff, lc;
40291         for(var i = 0, len = fs.length; i< len; i++){
40292             ff = fs[i];
40293             lc = ff.toLowerCase();
40294             buf.push(
40295                 '<option value="',lc,'" style="font-family:',ff,';"',
40296                     (this.defaultFont == lc ? ' selected="true">' : '>'),
40297                     ff,
40298                 '</option>'
40299             );
40300         }
40301         return buf.join('');
40302     },
40303     
40304     toggleSourceEdit : function(sourceEditMode){
40305         if(sourceEditMode === undefined){
40306             sourceEditMode = !this.sourceEditMode;
40307         }
40308         this.sourceEditMode = sourceEditMode === true;
40309         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
40310         // just toggle the button?
40311         if(btn.pressed !== this.editor.sourceEditMode){
40312             btn.toggle(this.editor.sourceEditMode);
40313             return;
40314         }
40315         
40316         if(this.sourceEditMode){
40317             this.tb.items.each(function(item){
40318                 if(item.cmd != 'sourceedit'){
40319                     item.disable();
40320                 }
40321             });
40322           
40323         }else{
40324             if(this.initialized){
40325                 this.tb.items.each(function(item){
40326                     item.enable();
40327                 });
40328             }
40329             
40330         }
40331         // tell the editor that it's been pressed..
40332         this.editor.toggleSourceEdit(sourceEditMode);
40333        
40334     },
40335      /**
40336      * Object collection of toolbar tooltips for the buttons in the editor. The key
40337      * is the command id associated with that button and the value is a valid QuickTips object.
40338      * For example:
40339 <pre><code>
40340 {
40341     bold : {
40342         title: 'Bold (Ctrl+B)',
40343         text: 'Make the selected text bold.',
40344         cls: 'x-html-editor-tip'
40345     },
40346     italic : {
40347         title: 'Italic (Ctrl+I)',
40348         text: 'Make the selected text italic.',
40349         cls: 'x-html-editor-tip'
40350     },
40351     ...
40352 </code></pre>
40353     * @type Object
40354      */
40355     buttonTips : {
40356         bold : {
40357             title: 'Bold (Ctrl+B)',
40358             text: 'Make the selected text bold.',
40359             cls: 'x-html-editor-tip'
40360         },
40361         italic : {
40362             title: 'Italic (Ctrl+I)',
40363             text: 'Make the selected text italic.',
40364             cls: 'x-html-editor-tip'
40365         },
40366         underline : {
40367             title: 'Underline (Ctrl+U)',
40368             text: 'Underline the selected text.',
40369             cls: 'x-html-editor-tip'
40370         },
40371         increasefontsize : {
40372             title: 'Grow Text',
40373             text: 'Increase the font size.',
40374             cls: 'x-html-editor-tip'
40375         },
40376         decreasefontsize : {
40377             title: 'Shrink Text',
40378             text: 'Decrease the font size.',
40379             cls: 'x-html-editor-tip'
40380         },
40381         backcolor : {
40382             title: 'Text Highlight Color',
40383             text: 'Change the background color of the selected text.',
40384             cls: 'x-html-editor-tip'
40385         },
40386         forecolor : {
40387             title: 'Font Color',
40388             text: 'Change the color of the selected text.',
40389             cls: 'x-html-editor-tip'
40390         },
40391         justifyleft : {
40392             title: 'Align Text Left',
40393             text: 'Align text to the left.',
40394             cls: 'x-html-editor-tip'
40395         },
40396         justifycenter : {
40397             title: 'Center Text',
40398             text: 'Center text in the editor.',
40399             cls: 'x-html-editor-tip'
40400         },
40401         justifyright : {
40402             title: 'Align Text Right',
40403             text: 'Align text to the right.',
40404             cls: 'x-html-editor-tip'
40405         },
40406         insertunorderedlist : {
40407             title: 'Bullet List',
40408             text: 'Start a bulleted list.',
40409             cls: 'x-html-editor-tip'
40410         },
40411         insertorderedlist : {
40412             title: 'Numbered List',
40413             text: 'Start a numbered list.',
40414             cls: 'x-html-editor-tip'
40415         },
40416         createlink : {
40417             title: 'Hyperlink',
40418             text: 'Make the selected text a hyperlink.',
40419             cls: 'x-html-editor-tip'
40420         },
40421         sourceedit : {
40422             title: 'Source Edit',
40423             text: 'Switch to source editing mode.',
40424             cls: 'x-html-editor-tip'
40425         }
40426     },
40427     // private
40428     onDestroy : function(){
40429         if(this.rendered){
40430             
40431             this.tb.items.each(function(item){
40432                 if(item.menu){
40433                     item.menu.removeAll();
40434                     if(item.menu.el){
40435                         item.menu.el.destroy();
40436                     }
40437                 }
40438                 item.destroy();
40439             });
40440              
40441         }
40442     },
40443     onFirstFocus: function() {
40444         this.tb.items.each(function(item){
40445            item.enable();
40446         });
40447     }
40448 });
40449
40450
40451
40452
40453 // <script type="text/javascript">
40454 /*
40455  * Based on
40456  * Ext JS Library 1.1.1
40457  * Copyright(c) 2006-2007, Ext JS, LLC.
40458  *  
40459  
40460  */
40461
40462  
40463 /**
40464  * @class Roo.form.HtmlEditor.ToolbarContext
40465  * Context Toolbar
40466  * 
40467  * Usage:
40468  *
40469  new Roo.form.HtmlEditor({
40470     ....
40471     toolbars : [
40472         { xtype: 'ToolbarStandard', styles : {} }
40473         { xtype: 'ToolbarContext', disable : {} }
40474     ]
40475 })
40476
40477      
40478  * 
40479  * @config : {Object} disable List of elements to disable.. (not done yet.)
40480  * @config : {Object} styles  Map of styles available.
40481  * 
40482  */
40483
40484 Roo.form.HtmlEditor.ToolbarContext = function(config)
40485 {
40486     
40487     Roo.apply(this, config);
40488     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
40489     // dont call parent... till later.
40490     this.styles = this.styles || {};
40491 }
40492 Roo.form.HtmlEditor.ToolbarContext.types = {
40493     'IMG' : {
40494         width : {
40495             title: "Width",
40496             width: 40
40497         },
40498         height:  {
40499             title: "Height",
40500             width: 40
40501         },
40502         align: {
40503             title: "Align",
40504             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
40505             width : 80
40506             
40507         },
40508         border: {
40509             title: "Border",
40510             width: 40
40511         },
40512         alt: {
40513             title: "Alt",
40514             width: 120
40515         },
40516         src : {
40517             title: "Src",
40518             width: 220
40519         }
40520         
40521     },
40522     'A' : {
40523         name : {
40524             title: "Name",
40525             width: 50
40526         },
40527         href:  {
40528             title: "Href",
40529             width: 220
40530         } // border?
40531         
40532     },
40533     'TABLE' : {
40534         rows : {
40535             title: "Rows",
40536             width: 20
40537         },
40538         cols : {
40539             title: "Cols",
40540             width: 20
40541         },
40542         width : {
40543             title: "Width",
40544             width: 40
40545         },
40546         height : {
40547             title: "Height",
40548             width: 40
40549         },
40550         border : {
40551             title: "Border",
40552             width: 20
40553         }
40554     },
40555     'TD' : {
40556         width : {
40557             title: "Width",
40558             width: 40
40559         },
40560         height : {
40561             title: "Height",
40562             width: 40
40563         },   
40564         align: {
40565             title: "Align",
40566             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
40567             width: 80
40568         },
40569         valign: {
40570             title: "Valign",
40571             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
40572             width: 80
40573         },
40574         colspan: {
40575             title: "Colspan",
40576             width: 20
40577             
40578         }
40579     },
40580     'INPUT' : {
40581         name : {
40582             title: "name",
40583             width: 120
40584         },
40585         value : {
40586             title: "Value",
40587             width: 120
40588         },
40589         width : {
40590             title: "Width",
40591             width: 40
40592         }
40593     },
40594     'LABEL' : {
40595         'for' : {
40596             title: "For",
40597             width: 120
40598         }
40599     },
40600     'TEXTAREA' : {
40601           name : {
40602             title: "name",
40603             width: 120
40604         },
40605         rows : {
40606             title: "Rows",
40607             width: 20
40608         },
40609         cols : {
40610             title: "Cols",
40611             width: 20
40612         }
40613     },
40614     'SELECT' : {
40615         name : {
40616             title: "name",
40617             width: 120
40618         },
40619         selectoptions : {
40620             title: "Options",
40621             width: 200
40622         }
40623     },
40624     
40625     // should we really allow this??
40626     // should this just be 
40627     'BODY' : {
40628         title : {
40629             title: "title",
40630             width: 200,
40631             disabled : true
40632         }
40633     },
40634     '*' : {
40635         // empty..
40636     }
40637 };
40638
40639
40640
40641 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
40642     
40643     tb: false,
40644     
40645     rendered: false,
40646     
40647     editor : false,
40648     /**
40649      * @cfg {Object} disable  List of toolbar elements to disable
40650          
40651      */
40652     disable : false,
40653     /**
40654      * @cfg {Object} styles List of styles 
40655      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
40656      *
40657      * These must be defined in the page, so they get rendered correctly..
40658      * .headline { }
40659      * TD.underline { }
40660      * 
40661      */
40662     styles : false,
40663     
40664     
40665     
40666     toolbars : false,
40667     
40668     init : function(editor)
40669     {
40670         this.editor = editor;
40671         
40672         
40673         var fid = editor.frameId;
40674         var etb = this;
40675         function btn(id, toggle, handler){
40676             var xid = fid + '-'+ id ;
40677             return {
40678                 id : xid,
40679                 cmd : id,
40680                 cls : 'x-btn-icon x-edit-'+id,
40681                 enableToggle:toggle !== false,
40682                 scope: editor, // was editor...
40683                 handler:handler||editor.relayBtnCmd,
40684                 clickEvent:'mousedown',
40685                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
40686                 tabIndex:-1
40687             };
40688         }
40689         // create a new element.
40690         var wdiv = editor.wrap.createChild({
40691                 tag: 'div'
40692             }, editor.wrap.dom.firstChild.nextSibling, true);
40693         
40694         // can we do this more than once??
40695         
40696          // stop form submits
40697       
40698  
40699         // disable everything...
40700         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
40701         this.toolbars = {};
40702            
40703         for (var i in  ty) {
40704           
40705             this.toolbars[i] = this.buildToolbar(ty[i],i);
40706         }
40707         this.tb = this.toolbars.BODY;
40708         this.tb.el.show();
40709         this.buildFooter();
40710         this.footer.show();
40711          
40712         this.rendered = true;
40713         
40714         // the all the btns;
40715         editor.on('editorevent', this.updateToolbar, this);
40716         // other toolbars need to implement this..
40717         //editor.on('editmodechange', this.updateToolbar, this);
40718     },
40719     
40720     
40721     
40722     /**
40723      * Protected method that will not generally be called directly. It triggers
40724      * a toolbar update by reading the markup state of the current selection in the editor.
40725      */
40726     updateToolbar: function(ignore_a,ignore_b,sel){
40727
40728         
40729         if(!this.editor.activated){
40730              this.editor.onFirstFocus();
40731             return;
40732         }
40733         var updateFooter = sel ? false : true;
40734         
40735         
40736         var ans = this.editor.getAllAncestors();
40737         
40738         // pick
40739         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
40740         
40741         if (!sel) { 
40742             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
40743             sel = sel ? sel : this.editor.doc.body;
40744             sel = sel.tagName.length ? sel : this.editor.doc.body;
40745             
40746         }
40747         // pick a menu that exists..
40748         var tn = sel.tagName.toUpperCase();
40749         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
40750         
40751         tn = sel.tagName.toUpperCase();
40752         
40753         var lastSel = this.tb.selectedNode
40754         
40755         this.tb.selectedNode = sel;
40756         
40757         // if current menu does not match..
40758         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
40759                 
40760             this.tb.el.hide();
40761             ///console.log("show: " + tn);
40762             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
40763             this.tb.el.show();
40764             // update name
40765             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
40766             
40767             
40768             // update attributes
40769             if (this.tb.fields) {
40770                 this.tb.fields.each(function(e) {
40771                    e.setValue(sel.getAttribute(e.name));
40772                 });
40773             }
40774             
40775             // update styles
40776             var st = this.tb.fields.item(0);
40777             st.store.removeAll();
40778             var cn = sel.className.split(/\s+/);
40779             
40780             var avs = [];
40781             if (this.styles['*']) {
40782                 
40783                 Roo.each(this.styles['*'], function(v) {
40784                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
40785                 });
40786             }
40787             if (this.styles[tn]) { 
40788                 Roo.each(this.styles[tn], function(v) {
40789                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
40790                 });
40791             }
40792             
40793             st.store.loadData(avs);
40794             st.collapse();
40795             st.setValue(cn);
40796             
40797             // flag our selected Node.
40798             this.tb.selectedNode = sel;
40799            
40800            
40801             Roo.menu.MenuMgr.hideAll();
40802
40803         }
40804         
40805         if (!updateFooter) {
40806             return;
40807         }
40808         // update the footer
40809         //
40810         var html = '';
40811         
40812         this.footerEls = ans.reverse();
40813         Roo.each(this.footerEls, function(a,i) {
40814             if (!a) { return; }
40815             html += html.length ? ' &gt; '  :  '';
40816             
40817             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
40818             
40819         });
40820        
40821         // 
40822         var sz = this.footDisp.up('td').getSize();
40823         this.footDisp.dom.style.width = (sz.width -10) + 'px';
40824         this.footDisp.dom.style.marginLeft = '5px';
40825         
40826         this.footDisp.dom.style.overflow = 'hidden';
40827         
40828         this.footDisp.dom.innerHTML = html;
40829             
40830         //this.editorsyncValue();
40831     },
40832    
40833        
40834     // private
40835     onDestroy : function(){
40836         if(this.rendered){
40837             
40838             this.tb.items.each(function(item){
40839                 if(item.menu){
40840                     item.menu.removeAll();
40841                     if(item.menu.el){
40842                         item.menu.el.destroy();
40843                     }
40844                 }
40845                 item.destroy();
40846             });
40847              
40848         }
40849     },
40850     onFirstFocus: function() {
40851         // need to do this for all the toolbars..
40852         this.tb.items.each(function(item){
40853            item.enable();
40854         });
40855     },
40856     buildToolbar: function(tlist, nm)
40857     {
40858         var editor = this.editor;
40859          // create a new element.
40860         var wdiv = editor.wrap.createChild({
40861                 tag: 'div'
40862             }, editor.wrap.dom.firstChild.nextSibling, true);
40863         
40864        
40865         var tb = new Roo.Toolbar(wdiv);
40866         // add the name..
40867         
40868         tb.add(nm+ ":&nbsp;");
40869         
40870         // styles...
40871         if (this.styles) {
40872             
40873             // this needs a multi-select checkbox...
40874             tb.addField( new Roo.form.ComboBox({
40875                 store: new Roo.data.SimpleStore({
40876                     id : 'val',
40877                     fields: ['val', 'selected'],
40878                     data : [] 
40879                 }),
40880                 name : 'className',
40881                 displayField:'val',
40882                 typeAhead: false,
40883                 mode: 'local',
40884                 editable : false,
40885                 triggerAction: 'all',
40886                 emptyText:'Select Style',
40887                 selectOnFocus:true,
40888                 width: 130,
40889                 listeners : {
40890                     'select': function(c, r, i) {
40891                         // initial support only for on class per el..
40892                         tb.selectedNode.className =  r ? r.get('val') : '';
40893                     }
40894                 }
40895     
40896             }));
40897         }
40898             
40899         
40900         
40901         for (var i in tlist) {
40902             
40903             var item = tlist[i];
40904             tb.add(item.title + ":&nbsp;");
40905             
40906             
40907             
40908             
40909             if (item.opts) {
40910                 // opts == pulldown..
40911                 tb.addField( new Roo.form.ComboBox({
40912                     store: new Roo.data.SimpleStore({
40913                         id : 'val',
40914                         fields: ['val'],
40915                         data : item.opts  
40916                     }),
40917                     name : i,
40918                     displayField:'val',
40919                     typeAhead: false,
40920                     mode: 'local',
40921                     editable : false,
40922                     triggerAction: 'all',
40923                     emptyText:'Select',
40924                     selectOnFocus:true,
40925                     width: item.width ? item.width  : 130,
40926                     listeners : {
40927                         'select': function(c, r, i) {
40928                             tb.selectedNode.setAttribute(c.name, r.get('val'));
40929                         }
40930                     }
40931
40932                 }));
40933                 continue;
40934                     
40935                  
40936                 
40937                 tb.addField( new Roo.form.TextField({
40938                     name: i,
40939                     width: 100,
40940                     //allowBlank:false,
40941                     value: ''
40942                 }));
40943                 continue;
40944             }
40945             tb.addField( new Roo.form.TextField({
40946                 name: i,
40947                 width: item.width,
40948                 //allowBlank:true,
40949                 value: '',
40950                 listeners: {
40951                     'change' : function(f, nv, ov) {
40952                         tb.selectedNode.setAttribute(f.name, nv);
40953                     }
40954                 }
40955             }));
40956              
40957         }
40958         tb.el.on('click', function(e){
40959             e.preventDefault(); // what does this do?
40960         });
40961         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
40962         tb.el.hide();
40963         tb.name = nm;
40964         // dont need to disable them... as they will get hidden
40965         return tb;
40966          
40967         
40968     },
40969     buildFooter : function()
40970     {
40971         
40972         var fel = this.editor.wrap.createChild();
40973         this.footer = new Roo.Toolbar(fel);
40974         // toolbar has scrolly on left / right?
40975         var footDisp= new Roo.Toolbar.Fill();
40976         var _t = this;
40977         this.footer.add(
40978             {
40979                 text : '&lt;',
40980                 xtype: 'Button',
40981                 handler : function() {
40982                     _t.footDisp.scrollTo('left',0,true)
40983                 }
40984             }
40985         );
40986         this.footer.add( footDisp );
40987         this.footer.add( 
40988             {
40989                 text : '&gt;',
40990                 xtype: 'Button',
40991                 handler : function() {
40992                     // no animation..
40993                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
40994                 }
40995             }
40996         );
40997         var fel = Roo.get(footDisp.el);
40998         fel.addClass('x-editor-context');
40999         this.footDispWrap = fel; 
41000         this.footDispWrap.overflow  = 'hidden';
41001         
41002         this.footDisp = fel.createChild();
41003         this.footDispWrap.on('click', this.onContextClick, this)
41004         
41005         
41006     },
41007     onContextClick : function (ev,dom)
41008     {
41009         ev.preventDefault();
41010         var  cn = dom.className;
41011         Roo.log(cn);
41012         if (!cn.match(/x-ed-loc-/)) {
41013             return;
41014         }
41015         var n = cn.split('-').pop();
41016         var ans = this.footerEls;
41017         var sel = ans[n];
41018         
41019          // pick
41020         var range = this.editor.createRange();
41021         
41022         range.selectNodeContents(sel);
41023         //range.selectNode(sel);
41024         
41025         
41026         var selection = this.editor.getSelection();
41027         selection.removeAllRanges();
41028         selection.addRange(range);
41029         
41030         
41031         
41032         this.updateToolbar(null, null, sel);
41033         
41034         
41035     }
41036     
41037     
41038     
41039     
41040     
41041 });
41042
41043
41044
41045
41046
41047 /*
41048  * Based on:
41049  * Ext JS Library 1.1.1
41050  * Copyright(c) 2006-2007, Ext JS, LLC.
41051  *
41052  * Originally Released Under LGPL - original licence link has changed is not relivant.
41053  *
41054  * Fork - LGPL
41055  * <script type="text/javascript">
41056  */
41057  
41058 /**
41059  * @class Roo.form.BasicForm
41060  * @extends Roo.util.Observable
41061  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
41062  * @constructor
41063  * @param {String/HTMLElement/Roo.Element} el The form element or its id
41064  * @param {Object} config Configuration options
41065  */
41066 Roo.form.BasicForm = function(el, config){
41067     this.allItems = [];
41068     this.childForms = [];
41069     Roo.apply(this, config);
41070     /*
41071      * The Roo.form.Field items in this form.
41072      * @type MixedCollection
41073      */
41074      
41075      
41076     this.items = new Roo.util.MixedCollection(false, function(o){
41077         return o.id || (o.id = Roo.id());
41078     });
41079     this.addEvents({
41080         /**
41081          * @event beforeaction
41082          * Fires before any action is performed. Return false to cancel the action.
41083          * @param {Form} this
41084          * @param {Action} action The action to be performed
41085          */
41086         beforeaction: true,
41087         /**
41088          * @event actionfailed
41089          * Fires when an action fails.
41090          * @param {Form} this
41091          * @param {Action} action The action that failed
41092          */
41093         actionfailed : true,
41094         /**
41095          * @event actioncomplete
41096          * Fires when an action is completed.
41097          * @param {Form} this
41098          * @param {Action} action The action that completed
41099          */
41100         actioncomplete : true
41101     });
41102     if(el){
41103         this.initEl(el);
41104     }
41105     Roo.form.BasicForm.superclass.constructor.call(this);
41106 };
41107
41108 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
41109     /**
41110      * @cfg {String} method
41111      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
41112      */
41113     /**
41114      * @cfg {DataReader} reader
41115      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
41116      * This is optional as there is built-in support for processing JSON.
41117      */
41118     /**
41119      * @cfg {DataReader} errorReader
41120      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
41121      * This is completely optional as there is built-in support for processing JSON.
41122      */
41123     /**
41124      * @cfg {String} url
41125      * The URL to use for form actions if one isn't supplied in the action options.
41126      */
41127     /**
41128      * @cfg {Boolean} fileUpload
41129      * Set to true if this form is a file upload.
41130      */
41131      
41132     /**
41133      * @cfg {Object} baseParams
41134      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
41135      */
41136      /**
41137      
41138     /**
41139      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
41140      */
41141     timeout: 30,
41142
41143     // private
41144     activeAction : null,
41145
41146     /**
41147      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
41148      * or setValues() data instead of when the form was first created.
41149      */
41150     trackResetOnLoad : false,
41151     
41152     
41153     /**
41154      * childForms - used for multi-tab forms
41155      * @type {Array}
41156      */
41157     childForms : false,
41158     
41159     /**
41160      * allItems - full list of fields.
41161      * @type {Array}
41162      */
41163     allItems : false,
41164     
41165     /**
41166      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
41167      * element by passing it or its id or mask the form itself by passing in true.
41168      * @type Mixed
41169      */
41170     waitMsgTarget : false,
41171
41172     // private
41173     initEl : function(el){
41174         this.el = Roo.get(el);
41175         this.id = this.el.id || Roo.id();
41176         this.el.on('submit', this.onSubmit, this);
41177         this.el.addClass('x-form');
41178     },
41179
41180     // private
41181     onSubmit : function(e){
41182         e.stopEvent();
41183     },
41184
41185     /**
41186      * Returns true if client-side validation on the form is successful.
41187      * @return Boolean
41188      */
41189     isValid : function(){
41190         var valid = true;
41191         this.items.each(function(f){
41192            if(!f.validate()){
41193                valid = false;
41194            }
41195         });
41196         return valid;
41197     },
41198
41199     /**
41200      * Returns true if any fields in this form have changed since their original load.
41201      * @return Boolean
41202      */
41203     isDirty : function(){
41204         var dirty = false;
41205         this.items.each(function(f){
41206            if(f.isDirty()){
41207                dirty = true;
41208                return false;
41209            }
41210         });
41211         return dirty;
41212     },
41213
41214     /**
41215      * Performs a predefined action (submit or load) or custom actions you define on this form.
41216      * @param {String} actionName The name of the action type
41217      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
41218      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
41219      * accept other config options):
41220      * <pre>
41221 Property          Type             Description
41222 ----------------  ---------------  ----------------------------------------------------------------------------------
41223 url               String           The url for the action (defaults to the form's url)
41224 method            String           The form method to use (defaults to the form's method, or POST if not defined)
41225 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
41226 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
41227                                    validate the form on the client (defaults to false)
41228      * </pre>
41229      * @return {BasicForm} this
41230      */
41231     doAction : function(action, options){
41232         if(typeof action == 'string'){
41233             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
41234         }
41235         if(this.fireEvent('beforeaction', this, action) !== false){
41236             this.beforeAction(action);
41237             action.run.defer(100, action);
41238         }
41239         return this;
41240     },
41241
41242     /**
41243      * Shortcut to do a submit action.
41244      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
41245      * @return {BasicForm} this
41246      */
41247     submit : function(options){
41248         this.doAction('submit', options);
41249         return this;
41250     },
41251
41252     /**
41253      * Shortcut to do a load action.
41254      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
41255      * @return {BasicForm} this
41256      */
41257     load : function(options){
41258         this.doAction('load', options);
41259         return this;
41260     },
41261
41262     /**
41263      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
41264      * @param {Record} record The record to edit
41265      * @return {BasicForm} this
41266      */
41267     updateRecord : function(record){
41268         record.beginEdit();
41269         var fs = record.fields;
41270         fs.each(function(f){
41271             var field = this.findField(f.name);
41272             if(field){
41273                 record.set(f.name, field.getValue());
41274             }
41275         }, this);
41276         record.endEdit();
41277         return this;
41278     },
41279
41280     /**
41281      * Loads an Roo.data.Record into this form.
41282      * @param {Record} record The record to load
41283      * @return {BasicForm} this
41284      */
41285     loadRecord : function(record){
41286         this.setValues(record.data);
41287         return this;
41288     },
41289
41290     // private
41291     beforeAction : function(action){
41292         var o = action.options;
41293         
41294        
41295         if(this.waitMsgTarget === true){
41296             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
41297         }else if(this.waitMsgTarget){
41298             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
41299             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
41300         }else {
41301             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
41302         }
41303          
41304     },
41305
41306     // private
41307     afterAction : function(action, success){
41308         this.activeAction = null;
41309         var o = action.options;
41310         
41311         if(this.waitMsgTarget === true){
41312             this.el.unmask();
41313         }else if(this.waitMsgTarget){
41314             this.waitMsgTarget.unmask();
41315         }else{
41316             Roo.MessageBox.updateProgress(1);
41317             Roo.MessageBox.hide();
41318         }
41319          
41320         if(success){
41321             if(o.reset){
41322                 this.reset();
41323             }
41324             Roo.callback(o.success, o.scope, [this, action]);
41325             this.fireEvent('actioncomplete', this, action);
41326             
41327         }else{
41328             Roo.callback(o.failure, o.scope, [this, action]);
41329             // show an error message if no failed handler is set..
41330             if (!this.hasListener('actionfailed')) {
41331                 Roo.MessageBox.alert("Error",
41332                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
41333                         action.result.errorMsg :
41334                         "Saving Failed, please check your entries"
41335                 );
41336             }
41337             
41338             this.fireEvent('actionfailed', this, action);
41339         }
41340         
41341     },
41342
41343     /**
41344      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
41345      * @param {String} id The value to search for
41346      * @return Field
41347      */
41348     findField : function(id){
41349         var field = this.items.get(id);
41350         if(!field){
41351             this.items.each(function(f){
41352                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
41353                     field = f;
41354                     return false;
41355                 }
41356             });
41357         }
41358         return field || null;
41359     },
41360
41361     /**
41362      * Add a secondary form to this one, 
41363      * Used to provide tabbed forms. One form is primary, with hidden values 
41364      * which mirror the elements from the other forms.
41365      * 
41366      * @param {Roo.form.Form} form to add.
41367      * 
41368      */
41369     addForm : function(form)
41370     {
41371        
41372         if (this.childForms.indexOf(form) > -1) {
41373             // already added..
41374             return;
41375         }
41376         this.childForms.push(form);
41377         var n = '';
41378         Roo.each(form.allItems, function (fe) {
41379             
41380             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
41381             if (this.findField(n)) { // already added..
41382                 return;
41383             }
41384             var add = new Roo.form.Hidden({
41385                 name : n
41386             });
41387             add.render(this.el);
41388             
41389             this.add( add );
41390         }, this);
41391         
41392     },
41393     /**
41394      * Mark fields in this form invalid in bulk.
41395      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
41396      * @return {BasicForm} this
41397      */
41398     markInvalid : function(errors){
41399         if(errors instanceof Array){
41400             for(var i = 0, len = errors.length; i < len; i++){
41401                 var fieldError = errors[i];
41402                 var f = this.findField(fieldError.id);
41403                 if(f){
41404                     f.markInvalid(fieldError.msg);
41405                 }
41406             }
41407         }else{
41408             var field, id;
41409             for(id in errors){
41410                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
41411                     field.markInvalid(errors[id]);
41412                 }
41413             }
41414         }
41415         Roo.each(this.childForms || [], function (f) {
41416             f.markInvalid(errors);
41417         });
41418         
41419         return this;
41420     },
41421
41422     /**
41423      * Set values for fields in this form in bulk.
41424      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
41425      * @return {BasicForm} this
41426      */
41427     setValues : function(values){
41428         if(values instanceof Array){ // array of objects
41429             for(var i = 0, len = values.length; i < len; i++){
41430                 var v = values[i];
41431                 var f = this.findField(v.id);
41432                 if(f){
41433                     f.setValue(v.value);
41434                     if(this.trackResetOnLoad){
41435                         f.originalValue = f.getValue();
41436                     }
41437                 }
41438             }
41439         }else{ // object hash
41440             var field, id;
41441             for(id in values){
41442                 if(typeof values[id] != 'function' && (field = this.findField(id))){
41443                     
41444                     if (field.setFromData && 
41445                         field.valueField && 
41446                         field.displayField &&
41447                         // combos' with local stores can 
41448                         // be queried via setValue()
41449                         // to set their value..
41450                         (field.store && !field.store.isLocal)
41451                         ) {
41452                         // it's a combo
41453                         var sd = { };
41454                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
41455                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
41456                         field.setFromData(sd);
41457                         
41458                     } else {
41459                         field.setValue(values[id]);
41460                     }
41461                     
41462                     
41463                     if(this.trackResetOnLoad){
41464                         field.originalValue = field.getValue();
41465                     }
41466                 }
41467             }
41468         }
41469          
41470         Roo.each(this.childForms || [], function (f) {
41471             f.setValues(values);
41472         });
41473                 
41474         return this;
41475     },
41476
41477     /**
41478      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
41479      * they are returned as an array.
41480      * @param {Boolean} asString
41481      * @return {Object}
41482      */
41483     getValues : function(asString){
41484         if (this.childForms) {
41485             // copy values from the child forms
41486             Roo.each(this.childForms, function (f) {
41487                 this.setValues(f.getValues());
41488             }, this);
41489         }
41490         
41491         
41492         
41493         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
41494         if(asString === true){
41495             return fs;
41496         }
41497         return Roo.urlDecode(fs);
41498     },
41499     
41500     /**
41501      * Returns the fields in this form as an object with key/value pairs. 
41502      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
41503      * @return {Object}
41504      */
41505     getFieldValues : function(with_hidden)
41506     {
41507         if (this.childForms) {
41508             // copy values from the child forms
41509             // should this call getFieldValues - probably not as we do not currently copy
41510             // hidden fields when we generate..
41511             Roo.each(this.childForms, function (f) {
41512                 this.setValues(f.getValues());
41513             }, this);
41514         }
41515         
41516         var ret = {};
41517         this.items.each(function(f){
41518             if (!f.getName()) {
41519                 return;
41520             }
41521             var v = f.getValue();
41522             // not sure if this supported any more..
41523             if ((typeof(v) == 'object') && f.getRawValue) {
41524                 v = f.getRawValue() ; // dates..
41525             }
41526             // combo boxes where name != hiddenName...
41527             if (f.name != f.getName()) {
41528                 ret[f.name] = f.getRawValue();
41529             }
41530             ret[f.getName()] = v;
41531         });
41532         
41533         return ret;
41534     },
41535
41536     /**
41537      * Clears all invalid messages in this form.
41538      * @return {BasicForm} this
41539      */
41540     clearInvalid : function(){
41541         this.items.each(function(f){
41542            f.clearInvalid();
41543         });
41544         
41545         Roo.each(this.childForms || [], function (f) {
41546             f.clearInvalid();
41547         });
41548         
41549         
41550         return this;
41551     },
41552
41553     /**
41554      * Resets this form.
41555      * @return {BasicForm} this
41556      */
41557     reset : function(){
41558         this.items.each(function(f){
41559             f.reset();
41560         });
41561         
41562         Roo.each(this.childForms || [], function (f) {
41563             f.reset();
41564         });
41565        
41566         
41567         return this;
41568     },
41569
41570     /**
41571      * Add Roo.form components to this form.
41572      * @param {Field} field1
41573      * @param {Field} field2 (optional)
41574      * @param {Field} etc (optional)
41575      * @return {BasicForm} this
41576      */
41577     add : function(){
41578         this.items.addAll(Array.prototype.slice.call(arguments, 0));
41579         return this;
41580     },
41581
41582
41583     /**
41584      * Removes a field from the items collection (does NOT remove its markup).
41585      * @param {Field} field
41586      * @return {BasicForm} this
41587      */
41588     remove : function(field){
41589         this.items.remove(field);
41590         return this;
41591     },
41592
41593     /**
41594      * Looks at the fields in this form, checks them for an id attribute,
41595      * and calls applyTo on the existing dom element with that id.
41596      * @return {BasicForm} this
41597      */
41598     render : function(){
41599         this.items.each(function(f){
41600             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
41601                 f.applyTo(f.id);
41602             }
41603         });
41604         return this;
41605     },
41606
41607     /**
41608      * Calls {@link Ext#apply} for all fields in this form with the passed object.
41609      * @param {Object} values
41610      * @return {BasicForm} this
41611      */
41612     applyToFields : function(o){
41613         this.items.each(function(f){
41614            Roo.apply(f, o);
41615         });
41616         return this;
41617     },
41618
41619     /**
41620      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
41621      * @param {Object} values
41622      * @return {BasicForm} this
41623      */
41624     applyIfToFields : function(o){
41625         this.items.each(function(f){
41626            Roo.applyIf(f, o);
41627         });
41628         return this;
41629     }
41630 });
41631
41632 // back compat
41633 Roo.BasicForm = Roo.form.BasicForm;/*
41634  * Based on:
41635  * Ext JS Library 1.1.1
41636  * Copyright(c) 2006-2007, Ext JS, LLC.
41637  *
41638  * Originally Released Under LGPL - original licence link has changed is not relivant.
41639  *
41640  * Fork - LGPL
41641  * <script type="text/javascript">
41642  */
41643
41644 /**
41645  * @class Roo.form.Form
41646  * @extends Roo.form.BasicForm
41647  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
41648  * @constructor
41649  * @param {Object} config Configuration options
41650  */
41651 Roo.form.Form = function(config){
41652     var xitems =  [];
41653     if (config.items) {
41654         xitems = config.items;
41655         delete config.items;
41656     }
41657    
41658     
41659     Roo.form.Form.superclass.constructor.call(this, null, config);
41660     this.url = this.url || this.action;
41661     if(!this.root){
41662         this.root = new Roo.form.Layout(Roo.applyIf({
41663             id: Roo.id()
41664         }, config));
41665     }
41666     this.active = this.root;
41667     /**
41668      * Array of all the buttons that have been added to this form via {@link addButton}
41669      * @type Array
41670      */
41671     this.buttons = [];
41672     this.allItems = [];
41673     this.addEvents({
41674         /**
41675          * @event clientvalidation
41676          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
41677          * @param {Form} this
41678          * @param {Boolean} valid true if the form has passed client-side validation
41679          */
41680         clientvalidation: true,
41681         /**
41682          * @event rendered
41683          * Fires when the form is rendered
41684          * @param {Roo.form.Form} form
41685          */
41686         rendered : true
41687     });
41688     
41689     if (this.progressUrl) {
41690             // push a hidden field onto the list of fields..
41691             this.addxtype( {
41692                     xns: Roo.form, 
41693                     xtype : 'Hidden', 
41694                     name : 'UPLOAD_IDENTIFIER' 
41695             });
41696         }
41697         
41698     
41699     Roo.each(xitems, this.addxtype, this);
41700     
41701     
41702     
41703 };
41704
41705 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
41706     /**
41707      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
41708      */
41709     /**
41710      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
41711      */
41712     /**
41713      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
41714      */
41715     buttonAlign:'center',
41716
41717     /**
41718      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
41719      */
41720     minButtonWidth:75,
41721
41722     /**
41723      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
41724      * This property cascades to child containers if not set.
41725      */
41726     labelAlign:'left',
41727
41728     /**
41729      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
41730      * fires a looping event with that state. This is required to bind buttons to the valid
41731      * state using the config value formBind:true on the button.
41732      */
41733     monitorValid : false,
41734
41735     /**
41736      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
41737      */
41738     monitorPoll : 200,
41739     
41740     /**
41741      * @cfg {String} progressUrl - Url to return progress data 
41742      */
41743     
41744     progressUrl : false,
41745   
41746     /**
41747      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
41748      * fields are added and the column is closed. If no fields are passed the column remains open
41749      * until end() is called.
41750      * @param {Object} config The config to pass to the column
41751      * @param {Field} field1 (optional)
41752      * @param {Field} field2 (optional)
41753      * @param {Field} etc (optional)
41754      * @return Column The column container object
41755      */
41756     column : function(c){
41757         var col = new Roo.form.Column(c);
41758         this.start(col);
41759         if(arguments.length > 1){ // duplicate code required because of Opera
41760             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
41761             this.end();
41762         }
41763         return col;
41764     },
41765
41766     /**
41767      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
41768      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
41769      * until end() is called.
41770      * @param {Object} config The config to pass to the fieldset
41771      * @param {Field} field1 (optional)
41772      * @param {Field} field2 (optional)
41773      * @param {Field} etc (optional)
41774      * @return FieldSet The fieldset container object
41775      */
41776     fieldset : function(c){
41777         var fs = new Roo.form.FieldSet(c);
41778         this.start(fs);
41779         if(arguments.length > 1){ // duplicate code required because of Opera
41780             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
41781             this.end();
41782         }
41783         return fs;
41784     },
41785
41786     /**
41787      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
41788      * fields are added and the container is closed. If no fields are passed the container remains open
41789      * until end() is called.
41790      * @param {Object} config The config to pass to the Layout
41791      * @param {Field} field1 (optional)
41792      * @param {Field} field2 (optional)
41793      * @param {Field} etc (optional)
41794      * @return Layout The container object
41795      */
41796     container : function(c){
41797         var l = new Roo.form.Layout(c);
41798         this.start(l);
41799         if(arguments.length > 1){ // duplicate code required because of Opera
41800             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
41801             this.end();
41802         }
41803         return l;
41804     },
41805
41806     /**
41807      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
41808      * @param {Object} container A Roo.form.Layout or subclass of Layout
41809      * @return {Form} this
41810      */
41811     start : function(c){
41812         // cascade label info
41813         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
41814         this.active.stack.push(c);
41815         c.ownerCt = this.active;
41816         this.active = c;
41817         return this;
41818     },
41819
41820     /**
41821      * Closes the current open container
41822      * @return {Form} this
41823      */
41824     end : function(){
41825         if(this.active == this.root){
41826             return this;
41827         }
41828         this.active = this.active.ownerCt;
41829         return this;
41830     },
41831
41832     /**
41833      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
41834      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
41835      * as the label of the field.
41836      * @param {Field} field1
41837      * @param {Field} field2 (optional)
41838      * @param {Field} etc. (optional)
41839      * @return {Form} this
41840      */
41841     add : function(){
41842         this.active.stack.push.apply(this.active.stack, arguments);
41843         this.allItems.push.apply(this.allItems,arguments);
41844         var r = [];
41845         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
41846             if(a[i].isFormField){
41847                 r.push(a[i]);
41848             }
41849         }
41850         if(r.length > 0){
41851             Roo.form.Form.superclass.add.apply(this, r);
41852         }
41853         return this;
41854     },
41855     
41856
41857     
41858     
41859     
41860      /**
41861      * Find any element that has been added to a form, using it's ID or name
41862      * This can include framesets, columns etc. along with regular fields..
41863      * @param {String} id - id or name to find.
41864      
41865      * @return {Element} e - or false if nothing found.
41866      */
41867     findbyId : function(id)
41868     {
41869         var ret = false;
41870         if (!id) {
41871             return ret;
41872         }
41873         Roo.each(this.allItems, function(f){
41874             if (f.id == id || f.name == id ){
41875                 ret = f;
41876                 return false;
41877             }
41878         });
41879         return ret;
41880     },
41881
41882     
41883     
41884     /**
41885      * Render this form into the passed container. This should only be called once!
41886      * @param {String/HTMLElement/Element} container The element this component should be rendered into
41887      * @return {Form} this
41888      */
41889     render : function(ct)
41890     {
41891         
41892         
41893         
41894         ct = Roo.get(ct);
41895         var o = this.autoCreate || {
41896             tag: 'form',
41897             method : this.method || 'POST',
41898             id : this.id || Roo.id()
41899         };
41900         this.initEl(ct.createChild(o));
41901
41902         this.root.render(this.el);
41903         
41904        
41905              
41906         this.items.each(function(f){
41907             f.render('x-form-el-'+f.id);
41908         });
41909
41910         if(this.buttons.length > 0){
41911             // tables are required to maintain order and for correct IE layout
41912             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
41913                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
41914                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
41915             }}, null, true);
41916             var tr = tb.getElementsByTagName('tr')[0];
41917             for(var i = 0, len = this.buttons.length; i < len; i++) {
41918                 var b = this.buttons[i];
41919                 var td = document.createElement('td');
41920                 td.className = 'x-form-btn-td';
41921                 b.render(tr.appendChild(td));
41922             }
41923         }
41924         if(this.monitorValid){ // initialize after render
41925             this.startMonitoring();
41926         }
41927         this.fireEvent('rendered', this);
41928         return this;
41929     },
41930
41931     /**
41932      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
41933      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
41934      * object or a valid Roo.DomHelper element config
41935      * @param {Function} handler The function called when the button is clicked
41936      * @param {Object} scope (optional) The scope of the handler function
41937      * @return {Roo.Button}
41938      */
41939     addButton : function(config, handler, scope){
41940         var bc = {
41941             handler: handler,
41942             scope: scope,
41943             minWidth: this.minButtonWidth,
41944             hideParent:true
41945         };
41946         if(typeof config == "string"){
41947             bc.text = config;
41948         }else{
41949             Roo.apply(bc, config);
41950         }
41951         var btn = new Roo.Button(null, bc);
41952         this.buttons.push(btn);
41953         return btn;
41954     },
41955
41956      /**
41957      * Adds a series of form elements (using the xtype property as the factory method.
41958      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
41959      * @param {Object} config 
41960      */
41961     
41962     addxtype : function()
41963     {
41964         var ar = Array.prototype.slice.call(arguments, 0);
41965         var ret = false;
41966         for(var i = 0; i < ar.length; i++) {
41967             if (!ar[i]) {
41968                 continue; // skip -- if this happends something invalid got sent, we 
41969                 // should ignore it, as basically that interface element will not show up
41970                 // and that should be pretty obvious!!
41971             }
41972             
41973             if (Roo.form[ar[i].xtype]) {
41974                 ar[i].form = this;
41975                 var fe = Roo.factory(ar[i], Roo.form);
41976                 if (!ret) {
41977                     ret = fe;
41978                 }
41979                 fe.form = this;
41980                 if (fe.store) {
41981                     fe.store.form = this;
41982                 }
41983                 if (fe.isLayout) {  
41984                          
41985                     this.start(fe);
41986                     this.allItems.push(fe);
41987                     if (fe.items && fe.addxtype) {
41988                         fe.addxtype.apply(fe, fe.items);
41989                         delete fe.items;
41990                     }
41991                      this.end();
41992                     continue;
41993                 }
41994                 
41995                 
41996                  
41997                 this.add(fe);
41998               //  console.log('adding ' + ar[i].xtype);
41999             }
42000             if (ar[i].xtype == 'Button') {  
42001                 //console.log('adding button');
42002                 //console.log(ar[i]);
42003                 this.addButton(ar[i]);
42004                 this.allItems.push(fe);
42005                 continue;
42006             }
42007             
42008             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
42009                 alert('end is not supported on xtype any more, use items');
42010             //    this.end();
42011             //    //console.log('adding end');
42012             }
42013             
42014         }
42015         return ret;
42016     },
42017     
42018     /**
42019      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
42020      * option "monitorValid"
42021      */
42022     startMonitoring : function(){
42023         if(!this.bound){
42024             this.bound = true;
42025             Roo.TaskMgr.start({
42026                 run : this.bindHandler,
42027                 interval : this.monitorPoll || 200,
42028                 scope: this
42029             });
42030         }
42031     },
42032
42033     /**
42034      * Stops monitoring of the valid state of this form
42035      */
42036     stopMonitoring : function(){
42037         this.bound = false;
42038     },
42039
42040     // private
42041     bindHandler : function(){
42042         if(!this.bound){
42043             return false; // stops binding
42044         }
42045         var valid = true;
42046         this.items.each(function(f){
42047             if(!f.isValid(true)){
42048                 valid = false;
42049                 return false;
42050             }
42051         });
42052         for(var i = 0, len = this.buttons.length; i < len; i++){
42053             var btn = this.buttons[i];
42054             if(btn.formBind === true && btn.disabled === valid){
42055                 btn.setDisabled(!valid);
42056             }
42057         }
42058         this.fireEvent('clientvalidation', this, valid);
42059     }
42060     
42061     
42062     
42063     
42064     
42065     
42066     
42067     
42068 });
42069
42070
42071 // back compat
42072 Roo.Form = Roo.form.Form;
42073 /*
42074  * Based on:
42075  * Ext JS Library 1.1.1
42076  * Copyright(c) 2006-2007, Ext JS, LLC.
42077  *
42078  * Originally Released Under LGPL - original licence link has changed is not relivant.
42079  *
42080  * Fork - LGPL
42081  * <script type="text/javascript">
42082  */
42083  
42084  /**
42085  * @class Roo.form.Action
42086  * Internal Class used to handle form actions
42087  * @constructor
42088  * @param {Roo.form.BasicForm} el The form element or its id
42089  * @param {Object} config Configuration options
42090  */
42091  
42092  
42093 // define the action interface
42094 Roo.form.Action = function(form, options){
42095     this.form = form;
42096     this.options = options || {};
42097 };
42098 /**
42099  * Client Validation Failed
42100  * @const 
42101  */
42102 Roo.form.Action.CLIENT_INVALID = 'client';
42103 /**
42104  * Server Validation Failed
42105  * @const 
42106  */
42107  Roo.form.Action.SERVER_INVALID = 'server';
42108  /**
42109  * Connect to Server Failed
42110  * @const 
42111  */
42112 Roo.form.Action.CONNECT_FAILURE = 'connect';
42113 /**
42114  * Reading Data from Server Failed
42115  * @const 
42116  */
42117 Roo.form.Action.LOAD_FAILURE = 'load';
42118
42119 Roo.form.Action.prototype = {
42120     type : 'default',
42121     failureType : undefined,
42122     response : undefined,
42123     result : undefined,
42124
42125     // interface method
42126     run : function(options){
42127
42128     },
42129
42130     // interface method
42131     success : function(response){
42132
42133     },
42134
42135     // interface method
42136     handleResponse : function(response){
42137
42138     },
42139
42140     // default connection failure
42141     failure : function(response){
42142         
42143         this.response = response;
42144         this.failureType = Roo.form.Action.CONNECT_FAILURE;
42145         this.form.afterAction(this, false);
42146     },
42147
42148     processResponse : function(response){
42149         this.response = response;
42150         if(!response.responseText){
42151             return true;
42152         }
42153         this.result = this.handleResponse(response);
42154         return this.result;
42155     },
42156
42157     // utility functions used internally
42158     getUrl : function(appendParams){
42159         var url = this.options.url || this.form.url || this.form.el.dom.action;
42160         if(appendParams){
42161             var p = this.getParams();
42162             if(p){
42163                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
42164             }
42165         }
42166         return url;
42167     },
42168
42169     getMethod : function(){
42170         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
42171     },
42172
42173     getParams : function(){
42174         var bp = this.form.baseParams;
42175         var p = this.options.params;
42176         if(p){
42177             if(typeof p == "object"){
42178                 p = Roo.urlEncode(Roo.applyIf(p, bp));
42179             }else if(typeof p == 'string' && bp){
42180                 p += '&' + Roo.urlEncode(bp);
42181             }
42182         }else if(bp){
42183             p = Roo.urlEncode(bp);
42184         }
42185         return p;
42186     },
42187
42188     createCallback : function(){
42189         return {
42190             success: this.success,
42191             failure: this.failure,
42192             scope: this,
42193             timeout: (this.form.timeout*1000),
42194             upload: this.form.fileUpload ? this.success : undefined
42195         };
42196     }
42197 };
42198
42199 Roo.form.Action.Submit = function(form, options){
42200     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
42201 };
42202
42203 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
42204     type : 'submit',
42205
42206     haveProgress : false,
42207     uploadComplete : false,
42208     
42209     // uploadProgress indicator.
42210     uploadProgress : function()
42211     {
42212         if (!this.form.progressUrl) {
42213             return;
42214         }
42215         
42216         if (!this.haveProgress) {
42217             Roo.MessageBox.progress("Uploading", "Uploading");
42218         }
42219         if (this.uploadComplete) {
42220            Roo.MessageBox.hide();
42221            return;
42222         }
42223         
42224         this.haveProgress = true;
42225    
42226         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
42227         
42228         var c = new Roo.data.Connection();
42229         c.request({
42230             url : this.form.progressUrl,
42231             params: {
42232                 id : uid
42233             },
42234             method: 'GET',
42235             success : function(req){
42236                //console.log(data);
42237                 var rdata = false;
42238                 var edata;
42239                 try  {
42240                    rdata = Roo.decode(req.responseText)
42241                 } catch (e) {
42242                     Roo.log("Invalid data from server..");
42243                     Roo.log(edata);
42244                     return;
42245                 }
42246                 if (!rdata || !rdata.success) {
42247                     Roo.log(rdata);
42248                     return;
42249                 }
42250                 var data = rdata.data;
42251                 
42252                 if (this.uploadComplete) {
42253                    Roo.MessageBox.hide();
42254                    return;
42255                 }
42256                    
42257                 if (data){
42258                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
42259                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
42260                     );
42261                 }
42262                 this.uploadProgress.defer(2000,this);
42263             },
42264        
42265             failure: function(data) {
42266                 Roo.log('progress url failed ');
42267                 Roo.log(data);
42268             },
42269             scope : this
42270         });
42271            
42272     },
42273     
42274     
42275     run : function()
42276     {
42277         // run get Values on the form, so it syncs any secondary forms.
42278         this.form.getValues();
42279         
42280         var o = this.options;
42281         var method = this.getMethod();
42282         var isPost = method == 'POST';
42283         if(o.clientValidation === false || this.form.isValid()){
42284             
42285             if (this.form.progressUrl) {
42286                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
42287                     (new Date() * 1) + '' + Math.random());
42288                     
42289             } 
42290             
42291             
42292             Roo.Ajax.request(Roo.apply(this.createCallback(), {
42293                 form:this.form.el.dom,
42294                 url:this.getUrl(!isPost),
42295                 method: method,
42296                 params:isPost ? this.getParams() : null,
42297                 isUpload: this.form.fileUpload
42298             }));
42299             
42300             this.uploadProgress();
42301
42302         }else if (o.clientValidation !== false){ // client validation failed
42303             this.failureType = Roo.form.Action.CLIENT_INVALID;
42304             this.form.afterAction(this, false);
42305         }
42306     },
42307
42308     success : function(response)
42309     {
42310         this.uploadComplete= true;
42311         if (this.haveProgress) {
42312             Roo.MessageBox.hide();
42313         }
42314         
42315         
42316         var result = this.processResponse(response);
42317         if(result === true || result.success){
42318             this.form.afterAction(this, true);
42319             return;
42320         }
42321         if(result.errors){
42322             this.form.markInvalid(result.errors);
42323             this.failureType = Roo.form.Action.SERVER_INVALID;
42324         }
42325         this.form.afterAction(this, false);
42326     },
42327     failure : function(response)
42328     {
42329         this.uploadComplete= true;
42330         if (this.haveProgress) {
42331             Roo.MessageBox.hide();
42332         }
42333         
42334         this.response = response;
42335         this.failureType = Roo.form.Action.CONNECT_FAILURE;
42336         this.form.afterAction(this, false);
42337     },
42338     
42339     handleResponse : function(response){
42340         if(this.form.errorReader){
42341             var rs = this.form.errorReader.read(response);
42342             var errors = [];
42343             if(rs.records){
42344                 for(var i = 0, len = rs.records.length; i < len; i++) {
42345                     var r = rs.records[i];
42346                     errors[i] = r.data;
42347                 }
42348             }
42349             if(errors.length < 1){
42350                 errors = null;
42351             }
42352             return {
42353                 success : rs.success,
42354                 errors : errors
42355             };
42356         }
42357         var ret = false;
42358         try {
42359             ret = Roo.decode(response.responseText);
42360         } catch (e) {
42361             ret = {
42362                 success: false,
42363                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
42364                 errors : []
42365             };
42366         }
42367         return ret;
42368         
42369     }
42370 });
42371
42372
42373 Roo.form.Action.Load = function(form, options){
42374     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
42375     this.reader = this.form.reader;
42376 };
42377
42378 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
42379     type : 'load',
42380
42381     run : function(){
42382         
42383         Roo.Ajax.request(Roo.apply(
42384                 this.createCallback(), {
42385                     method:this.getMethod(),
42386                     url:this.getUrl(false),
42387                     params:this.getParams()
42388         }));
42389     },
42390
42391     success : function(response){
42392         
42393         var result = this.processResponse(response);
42394         if(result === true || !result.success || !result.data){
42395             this.failureType = Roo.form.Action.LOAD_FAILURE;
42396             this.form.afterAction(this, false);
42397             return;
42398         }
42399         this.form.clearInvalid();
42400         this.form.setValues(result.data);
42401         this.form.afterAction(this, true);
42402     },
42403
42404     handleResponse : function(response){
42405         if(this.form.reader){
42406             var rs = this.form.reader.read(response);
42407             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
42408             return {
42409                 success : rs.success,
42410                 data : data
42411             };
42412         }
42413         return Roo.decode(response.responseText);
42414     }
42415 });
42416
42417 Roo.form.Action.ACTION_TYPES = {
42418     'load' : Roo.form.Action.Load,
42419     'submit' : Roo.form.Action.Submit
42420 };/*
42421  * Based on:
42422  * Ext JS Library 1.1.1
42423  * Copyright(c) 2006-2007, Ext JS, LLC.
42424  *
42425  * Originally Released Under LGPL - original licence link has changed is not relivant.
42426  *
42427  * Fork - LGPL
42428  * <script type="text/javascript">
42429  */
42430  
42431 /**
42432  * @class Roo.form.Layout
42433  * @extends Roo.Component
42434  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
42435  * @constructor
42436  * @param {Object} config Configuration options
42437  */
42438 Roo.form.Layout = function(config){
42439     var xitems = [];
42440     if (config.items) {
42441         xitems = config.items;
42442         delete config.items;
42443     }
42444     Roo.form.Layout.superclass.constructor.call(this, config);
42445     this.stack = [];
42446     Roo.each(xitems, this.addxtype, this);
42447      
42448 };
42449
42450 Roo.extend(Roo.form.Layout, Roo.Component, {
42451     /**
42452      * @cfg {String/Object} autoCreate
42453      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
42454      */
42455     /**
42456      * @cfg {String/Object/Function} style
42457      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
42458      * a function which returns such a specification.
42459      */
42460     /**
42461      * @cfg {String} labelAlign
42462      * Valid values are "left," "top" and "right" (defaults to "left")
42463      */
42464     /**
42465      * @cfg {Number} labelWidth
42466      * Fixed width in pixels of all field labels (defaults to undefined)
42467      */
42468     /**
42469      * @cfg {Boolean} clear
42470      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
42471      */
42472     clear : true,
42473     /**
42474      * @cfg {String} labelSeparator
42475      * The separator to use after field labels (defaults to ':')
42476      */
42477     labelSeparator : ':',
42478     /**
42479      * @cfg {Boolean} hideLabels
42480      * True to suppress the display of field labels in this layout (defaults to false)
42481      */
42482     hideLabels : false,
42483
42484     // private
42485     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
42486     
42487     isLayout : true,
42488     
42489     // private
42490     onRender : function(ct, position){
42491         if(this.el){ // from markup
42492             this.el = Roo.get(this.el);
42493         }else {  // generate
42494             var cfg = this.getAutoCreate();
42495             this.el = ct.createChild(cfg, position);
42496         }
42497         if(this.style){
42498             this.el.applyStyles(this.style);
42499         }
42500         if(this.labelAlign){
42501             this.el.addClass('x-form-label-'+this.labelAlign);
42502         }
42503         if(this.hideLabels){
42504             this.labelStyle = "display:none";
42505             this.elementStyle = "padding-left:0;";
42506         }else{
42507             if(typeof this.labelWidth == 'number'){
42508                 this.labelStyle = "width:"+this.labelWidth+"px;";
42509                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
42510             }
42511             if(this.labelAlign == 'top'){
42512                 this.labelStyle = "width:auto;";
42513                 this.elementStyle = "padding-left:0;";
42514             }
42515         }
42516         var stack = this.stack;
42517         var slen = stack.length;
42518         if(slen > 0){
42519             if(!this.fieldTpl){
42520                 var t = new Roo.Template(
42521                     '<div class="x-form-item {5}">',
42522                         '<label for="{0}" style="{2}">{1}{4}</label>',
42523                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
42524                         '</div>',
42525                     '</div><div class="x-form-clear-left"></div>'
42526                 );
42527                 t.disableFormats = true;
42528                 t.compile();
42529                 Roo.form.Layout.prototype.fieldTpl = t;
42530             }
42531             for(var i = 0; i < slen; i++) {
42532                 if(stack[i].isFormField){
42533                     this.renderField(stack[i]);
42534                 }else{
42535                     this.renderComponent(stack[i]);
42536                 }
42537             }
42538         }
42539         if(this.clear){
42540             this.el.createChild({cls:'x-form-clear'});
42541         }
42542     },
42543
42544     // private
42545     renderField : function(f){
42546         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
42547                f.id, //0
42548                f.fieldLabel, //1
42549                f.labelStyle||this.labelStyle||'', //2
42550                this.elementStyle||'', //3
42551                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
42552                f.itemCls||this.itemCls||''  //5
42553        ], true).getPrevSibling());
42554     },
42555
42556     // private
42557     renderComponent : function(c){
42558         c.render(c.isLayout ? this.el : this.el.createChild());    
42559     },
42560     /**
42561      * Adds a object form elements (using the xtype property as the factory method.)
42562      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
42563      * @param {Object} config 
42564      */
42565     addxtype : function(o)
42566     {
42567         // create the lement.
42568         o.form = this.form;
42569         var fe = Roo.factory(o, Roo.form);
42570         this.form.allItems.push(fe);
42571         this.stack.push(fe);
42572         
42573         if (fe.isFormField) {
42574             this.form.items.add(fe);
42575         }
42576          
42577         return fe;
42578     }
42579 });
42580
42581 /**
42582  * @class Roo.form.Column
42583  * @extends Roo.form.Layout
42584  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
42585  * @constructor
42586  * @param {Object} config Configuration options
42587  */
42588 Roo.form.Column = function(config){
42589     Roo.form.Column.superclass.constructor.call(this, config);
42590 };
42591
42592 Roo.extend(Roo.form.Column, Roo.form.Layout, {
42593     /**
42594      * @cfg {Number/String} width
42595      * The fixed width of the column in pixels or CSS value (defaults to "auto")
42596      */
42597     /**
42598      * @cfg {String/Object} autoCreate
42599      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
42600      */
42601
42602     // private
42603     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
42604
42605     // private
42606     onRender : function(ct, position){
42607         Roo.form.Column.superclass.onRender.call(this, ct, position);
42608         if(this.width){
42609             this.el.setWidth(this.width);
42610         }
42611     }
42612 });
42613
42614
42615 /**
42616  * @class Roo.form.Row
42617  * @extends Roo.form.Layout
42618  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
42619  * @constructor
42620  * @param {Object} config Configuration options
42621  */
42622
42623  
42624 Roo.form.Row = function(config){
42625     Roo.form.Row.superclass.constructor.call(this, config);
42626 };
42627  
42628 Roo.extend(Roo.form.Row, Roo.form.Layout, {
42629       /**
42630      * @cfg {Number/String} width
42631      * The fixed width of the column in pixels or CSS value (defaults to "auto")
42632      */
42633     /**
42634      * @cfg {Number/String} height
42635      * The fixed height of the column in pixels or CSS value (defaults to "auto")
42636      */
42637     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
42638     
42639     padWidth : 20,
42640     // private
42641     onRender : function(ct, position){
42642         //console.log('row render');
42643         if(!this.rowTpl){
42644             var t = new Roo.Template(
42645                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
42646                     '<label for="{0}" style="{2}">{1}{4}</label>',
42647                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
42648                     '</div>',
42649                 '</div>'
42650             );
42651             t.disableFormats = true;
42652             t.compile();
42653             Roo.form.Layout.prototype.rowTpl = t;
42654         }
42655         this.fieldTpl = this.rowTpl;
42656         
42657         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
42658         var labelWidth = 100;
42659         
42660         if ((this.labelAlign != 'top')) {
42661             if (typeof this.labelWidth == 'number') {
42662                 labelWidth = this.labelWidth
42663             }
42664             this.padWidth =  20 + labelWidth;
42665             
42666         }
42667         
42668         Roo.form.Column.superclass.onRender.call(this, ct, position);
42669         if(this.width){
42670             this.el.setWidth(this.width);
42671         }
42672         if(this.height){
42673             this.el.setHeight(this.height);
42674         }
42675     },
42676     
42677     // private
42678     renderField : function(f){
42679         f.fieldEl = this.fieldTpl.append(this.el, [
42680                f.id, f.fieldLabel,
42681                f.labelStyle||this.labelStyle||'',
42682                this.elementStyle||'',
42683                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
42684                f.itemCls||this.itemCls||'',
42685                f.width ? f.width + this.padWidth : 160 + this.padWidth
42686        ],true);
42687     }
42688 });
42689  
42690
42691 /**
42692  * @class Roo.form.FieldSet
42693  * @extends Roo.form.Layout
42694  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
42695  * @constructor
42696  * @param {Object} config Configuration options
42697  */
42698 Roo.form.FieldSet = function(config){
42699     Roo.form.FieldSet.superclass.constructor.call(this, config);
42700 };
42701
42702 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
42703     /**
42704      * @cfg {String} legend
42705      * The text to display as the legend for the FieldSet (defaults to '')
42706      */
42707     /**
42708      * @cfg {String/Object} autoCreate
42709      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
42710      */
42711
42712     // private
42713     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
42714
42715     // private
42716     onRender : function(ct, position){
42717         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
42718         if(this.legend){
42719             this.setLegend(this.legend);
42720         }
42721     },
42722
42723     // private
42724     setLegend : function(text){
42725         if(this.rendered){
42726             this.el.child('legend').update(text);
42727         }
42728     }
42729 });/*
42730  * Based on:
42731  * Ext JS Library 1.1.1
42732  * Copyright(c) 2006-2007, Ext JS, LLC.
42733  *
42734  * Originally Released Under LGPL - original licence link has changed is not relivant.
42735  *
42736  * Fork - LGPL
42737  * <script type="text/javascript">
42738  */
42739 /**
42740  * @class Roo.form.VTypes
42741  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
42742  * @singleton
42743  */
42744 Roo.form.VTypes = function(){
42745     // closure these in so they are only created once.
42746     var alpha = /^[a-zA-Z_]+$/;
42747     var alphanum = /^[a-zA-Z0-9_]+$/;
42748     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
42749     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
42750
42751     // All these messages and functions are configurable
42752     return {
42753         /**
42754          * The function used to validate email addresses
42755          * @param {String} value The email address
42756          */
42757         'email' : function(v){
42758             return email.test(v);
42759         },
42760         /**
42761          * The error text to display when the email validation function returns false
42762          * @type String
42763          */
42764         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
42765         /**
42766          * The keystroke filter mask to be applied on email input
42767          * @type RegExp
42768          */
42769         'emailMask' : /[a-z0-9_\.\-@]/i,
42770
42771         /**
42772          * The function used to validate URLs
42773          * @param {String} value The URL
42774          */
42775         'url' : function(v){
42776             return url.test(v);
42777         },
42778         /**
42779          * The error text to display when the url validation function returns false
42780          * @type String
42781          */
42782         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
42783         
42784         /**
42785          * The function used to validate alpha values
42786          * @param {String} value The value
42787          */
42788         'alpha' : function(v){
42789             return alpha.test(v);
42790         },
42791         /**
42792          * The error text to display when the alpha validation function returns false
42793          * @type String
42794          */
42795         'alphaText' : 'This field should only contain letters and _',
42796         /**
42797          * The keystroke filter mask to be applied on alpha input
42798          * @type RegExp
42799          */
42800         'alphaMask' : /[a-z_]/i,
42801
42802         /**
42803          * The function used to validate alphanumeric values
42804          * @param {String} value The value
42805          */
42806         'alphanum' : function(v){
42807             return alphanum.test(v);
42808         },
42809         /**
42810          * The error text to display when the alphanumeric validation function returns false
42811          * @type String
42812          */
42813         'alphanumText' : 'This field should only contain letters, numbers and _',
42814         /**
42815          * The keystroke filter mask to be applied on alphanumeric input
42816          * @type RegExp
42817          */
42818         'alphanumMask' : /[a-z0-9_]/i
42819     };
42820 }();//<script type="text/javascript">
42821
42822 /**
42823  * @class Roo.form.FCKeditor
42824  * @extends Roo.form.TextArea
42825  * Wrapper around the FCKEditor http://www.fckeditor.net
42826  * @constructor
42827  * Creates a new FCKeditor
42828  * @param {Object} config Configuration options
42829  */
42830 Roo.form.FCKeditor = function(config){
42831     Roo.form.FCKeditor.superclass.constructor.call(this, config);
42832     this.addEvents({
42833          /**
42834          * @event editorinit
42835          * Fired when the editor is initialized - you can add extra handlers here..
42836          * @param {FCKeditor} this
42837          * @param {Object} the FCK object.
42838          */
42839         editorinit : true
42840     });
42841     
42842     
42843 };
42844 Roo.form.FCKeditor.editors = { };
42845 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
42846 {
42847     //defaultAutoCreate : {
42848     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
42849     //},
42850     // private
42851     /**
42852      * @cfg {Object} fck options - see fck manual for details.
42853      */
42854     fckconfig : false,
42855     
42856     /**
42857      * @cfg {Object} fck toolbar set (Basic or Default)
42858      */
42859     toolbarSet : 'Basic',
42860     /**
42861      * @cfg {Object} fck BasePath
42862      */ 
42863     basePath : '/fckeditor/',
42864     
42865     
42866     frame : false,
42867     
42868     value : '',
42869     
42870    
42871     onRender : function(ct, position)
42872     {
42873         if(!this.el){
42874             this.defaultAutoCreate = {
42875                 tag: "textarea",
42876                 style:"width:300px;height:60px;",
42877                 autocomplete: "off"
42878             };
42879         }
42880         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
42881         /*
42882         if(this.grow){
42883             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
42884             if(this.preventScrollbars){
42885                 this.el.setStyle("overflow", "hidden");
42886             }
42887             this.el.setHeight(this.growMin);
42888         }
42889         */
42890         //console.log('onrender' + this.getId() );
42891         Roo.form.FCKeditor.editors[this.getId()] = this;
42892          
42893
42894         this.replaceTextarea() ;
42895         
42896     },
42897     
42898     getEditor : function() {
42899         return this.fckEditor;
42900     },
42901     /**
42902      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
42903      * @param {Mixed} value The value to set
42904      */
42905     
42906     
42907     setValue : function(value)
42908     {
42909         //console.log('setValue: ' + value);
42910         
42911         if(typeof(value) == 'undefined') { // not sure why this is happending...
42912             return;
42913         }
42914         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
42915         
42916         //if(!this.el || !this.getEditor()) {
42917         //    this.value = value;
42918             //this.setValue.defer(100,this,[value]);    
42919         //    return;
42920         //} 
42921         
42922         if(!this.getEditor()) {
42923             return;
42924         }
42925         
42926         this.getEditor().SetData(value);
42927         
42928         //
42929
42930     },
42931
42932     /**
42933      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
42934      * @return {Mixed} value The field value
42935      */
42936     getValue : function()
42937     {
42938         
42939         if (this.frame && this.frame.dom.style.display == 'none') {
42940             return Roo.form.FCKeditor.superclass.getValue.call(this);
42941         }
42942         
42943         if(!this.el || !this.getEditor()) {
42944            
42945            // this.getValue.defer(100,this); 
42946             return this.value;
42947         }
42948        
42949         
42950         var value=this.getEditor().GetData();
42951         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
42952         return Roo.form.FCKeditor.superclass.getValue.call(this);
42953         
42954
42955     },
42956
42957     /**
42958      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
42959      * @return {Mixed} value The field value
42960      */
42961     getRawValue : function()
42962     {
42963         if (this.frame && this.frame.dom.style.display == 'none') {
42964             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
42965         }
42966         
42967         if(!this.el || !this.getEditor()) {
42968             //this.getRawValue.defer(100,this); 
42969             return this.value;
42970             return;
42971         }
42972         
42973         
42974         
42975         var value=this.getEditor().GetData();
42976         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
42977         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
42978          
42979     },
42980     
42981     setSize : function(w,h) {
42982         
42983         
42984         
42985         //if (this.frame && this.frame.dom.style.display == 'none') {
42986         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
42987         //    return;
42988         //}
42989         //if(!this.el || !this.getEditor()) {
42990         //    this.setSize.defer(100,this, [w,h]); 
42991         //    return;
42992         //}
42993         
42994         
42995         
42996         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
42997         
42998         this.frame.dom.setAttribute('width', w);
42999         this.frame.dom.setAttribute('height', h);
43000         this.frame.setSize(w,h);
43001         
43002     },
43003     
43004     toggleSourceEdit : function(value) {
43005         
43006       
43007          
43008         this.el.dom.style.display = value ? '' : 'none';
43009         this.frame.dom.style.display = value ?  'none' : '';
43010         
43011     },
43012     
43013     
43014     focus: function(tag)
43015     {
43016         if (this.frame.dom.style.display == 'none') {
43017             return Roo.form.FCKeditor.superclass.focus.call(this);
43018         }
43019         if(!this.el || !this.getEditor()) {
43020             this.focus.defer(100,this, [tag]); 
43021             return;
43022         }
43023         
43024         
43025         
43026         
43027         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
43028         this.getEditor().Focus();
43029         if (tgs.length) {
43030             if (!this.getEditor().Selection.GetSelection()) {
43031                 this.focus.defer(100,this, [tag]); 
43032                 return;
43033             }
43034             
43035             
43036             var r = this.getEditor().EditorDocument.createRange();
43037             r.setStart(tgs[0],0);
43038             r.setEnd(tgs[0],0);
43039             this.getEditor().Selection.GetSelection().removeAllRanges();
43040             this.getEditor().Selection.GetSelection().addRange(r);
43041             this.getEditor().Focus();
43042         }
43043         
43044     },
43045     
43046     
43047     
43048     replaceTextarea : function()
43049     {
43050         if ( document.getElementById( this.getId() + '___Frame' ) )
43051             return ;
43052         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
43053         //{
43054             // We must check the elements firstly using the Id and then the name.
43055         var oTextarea = document.getElementById( this.getId() );
43056         
43057         var colElementsByName = document.getElementsByName( this.getId() ) ;
43058          
43059         oTextarea.style.display = 'none' ;
43060
43061         if ( oTextarea.tabIndex ) {            
43062             this.TabIndex = oTextarea.tabIndex ;
43063         }
43064         
43065         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
43066         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
43067         this.frame = Roo.get(this.getId() + '___Frame')
43068     },
43069     
43070     _getConfigHtml : function()
43071     {
43072         var sConfig = '' ;
43073
43074         for ( var o in this.fckconfig ) {
43075             sConfig += sConfig.length > 0  ? '&amp;' : '';
43076             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
43077         }
43078
43079         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
43080     },
43081     
43082     
43083     _getIFrameHtml : function()
43084     {
43085         var sFile = 'fckeditor.html' ;
43086         /* no idea what this is about..
43087         try
43088         {
43089             if ( (/fcksource=true/i).test( window.top.location.search ) )
43090                 sFile = 'fckeditor.original.html' ;
43091         }
43092         catch (e) { 
43093         */
43094
43095         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
43096         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
43097         
43098         
43099         var html = '<iframe id="' + this.getId() +
43100             '___Frame" src="' + sLink +
43101             '" width="' + this.width +
43102             '" height="' + this.height + '"' +
43103             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
43104             ' frameborder="0" scrolling="no"></iframe>' ;
43105
43106         return html ;
43107     },
43108     
43109     _insertHtmlBefore : function( html, element )
43110     {
43111         if ( element.insertAdjacentHTML )       {
43112             // IE
43113             element.insertAdjacentHTML( 'beforeBegin', html ) ;
43114         } else { // Gecko
43115             var oRange = document.createRange() ;
43116             oRange.setStartBefore( element ) ;
43117             var oFragment = oRange.createContextualFragment( html );
43118             element.parentNode.insertBefore( oFragment, element ) ;
43119         }
43120     }
43121     
43122     
43123   
43124     
43125     
43126     
43127     
43128
43129 });
43130
43131 //Roo.reg('fckeditor', Roo.form.FCKeditor);
43132
43133 function FCKeditor_OnComplete(editorInstance){
43134     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
43135     f.fckEditor = editorInstance;
43136     //console.log("loaded");
43137     f.fireEvent('editorinit', f, editorInstance);
43138
43139   
43140
43141  
43142
43143
43144
43145
43146
43147
43148
43149
43150
43151
43152
43153
43154
43155
43156
43157 //<script type="text/javascript">
43158 /**
43159  * @class Roo.form.GridField
43160  * @extends Roo.form.Field
43161  * Embed a grid (or editable grid into a form)
43162  * STATUS ALPHA
43163  * 
43164  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
43165  * it needs 
43166  * xgrid.store = Roo.data.Store
43167  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
43168  * xgrid.store.reader = Roo.data.JsonReader 
43169  * 
43170  * 
43171  * @constructor
43172  * Creates a new GridField
43173  * @param {Object} config Configuration options
43174  */
43175 Roo.form.GridField = function(config){
43176     Roo.form.GridField.superclass.constructor.call(this, config);
43177      
43178 };
43179
43180 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
43181     /**
43182      * @cfg {Number} width  - used to restrict width of grid..
43183      */
43184     width : 100,
43185     /**
43186      * @cfg {Number} height - used to restrict height of grid..
43187      */
43188     height : 50,
43189      /**
43190      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
43191          * 
43192          *}
43193      */
43194     xgrid : false, 
43195     /**
43196      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
43197      * {tag: "input", type: "checkbox", autocomplete: "off"})
43198      */
43199    // defaultAutoCreate : { tag: 'div' },
43200     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
43201     /**
43202      * @cfg {String} addTitle Text to include for adding a title.
43203      */
43204     addTitle : false,
43205     //
43206     onResize : function(){
43207         Roo.form.Field.superclass.onResize.apply(this, arguments);
43208     },
43209
43210     initEvents : function(){
43211         // Roo.form.Checkbox.superclass.initEvents.call(this);
43212         // has no events...
43213        
43214     },
43215
43216
43217     getResizeEl : function(){
43218         return this.wrap;
43219     },
43220
43221     getPositionEl : function(){
43222         return this.wrap;
43223     },
43224
43225     // private
43226     onRender : function(ct, position){
43227         
43228         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
43229         var style = this.style;
43230         delete this.style;
43231         
43232         Roo.form.GridField.superclass.onRender.call(this, ct, position);
43233         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
43234         this.viewEl = this.wrap.createChild({ tag: 'div' });
43235         if (style) {
43236             this.viewEl.applyStyles(style);
43237         }
43238         if (this.width) {
43239             this.viewEl.setWidth(this.width);
43240         }
43241         if (this.height) {
43242             this.viewEl.setHeight(this.height);
43243         }
43244         //if(this.inputValue !== undefined){
43245         //this.setValue(this.value);
43246         
43247         
43248         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
43249         
43250         
43251         this.grid.render();
43252         this.grid.getDataSource().on('remove', this.refreshValue, this);
43253         this.grid.getDataSource().on('update', this.refreshValue, this);
43254         this.grid.on('afteredit', this.refreshValue, this);
43255  
43256     },
43257      
43258     
43259     /**
43260      * Sets the value of the item. 
43261      * @param {String} either an object  or a string..
43262      */
43263     setValue : function(v){
43264         //this.value = v;
43265         v = v || []; // empty set..
43266         // this does not seem smart - it really only affects memoryproxy grids..
43267         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
43268             var ds = this.grid.getDataSource();
43269             // assumes a json reader..
43270             var data = {}
43271             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
43272             ds.loadData( data);
43273         }
43274         // clear selection so it does not get stale.
43275         if (this.grid.sm) { 
43276             this.grid.sm.clearSelections();
43277         }
43278         
43279         Roo.form.GridField.superclass.setValue.call(this, v);
43280         this.refreshValue();
43281         // should load data in the grid really....
43282     },
43283     
43284     // private
43285     refreshValue: function() {
43286          var val = [];
43287         this.grid.getDataSource().each(function(r) {
43288             val.push(r.data);
43289         });
43290         this.el.dom.value = Roo.encode(val);
43291     }
43292     
43293      
43294     
43295     
43296 });/*
43297  * Based on:
43298  * Ext JS Library 1.1.1
43299  * Copyright(c) 2006-2007, Ext JS, LLC.
43300  *
43301  * Originally Released Under LGPL - original licence link has changed is not relivant.
43302  *
43303  * Fork - LGPL
43304  * <script type="text/javascript">
43305  */
43306 /**
43307  * @class Roo.form.DisplayField
43308  * @extends Roo.form.Field
43309  * A generic Field to display non-editable data.
43310  * @constructor
43311  * Creates a new Display Field item.
43312  * @param {Object} config Configuration options
43313  */
43314 Roo.form.DisplayField = function(config){
43315     Roo.form.DisplayField.superclass.constructor.call(this, config);
43316     
43317 };
43318
43319 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
43320     inputType:      'hidden',
43321     allowBlank:     true,
43322     readOnly:         true,
43323     
43324  
43325     /**
43326      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
43327      */
43328     focusClass : undefined,
43329     /**
43330      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
43331      */
43332     fieldClass: 'x-form-field',
43333     
43334      /**
43335      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
43336      */
43337     valueRenderer: undefined,
43338     
43339     width: 100,
43340     /**
43341      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
43342      * {tag: "input", type: "checkbox", autocomplete: "off"})
43343      */
43344      
43345  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
43346
43347     onResize : function(){
43348         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
43349         
43350     },
43351
43352     initEvents : function(){
43353         // Roo.form.Checkbox.superclass.initEvents.call(this);
43354         // has no events...
43355        
43356     },
43357
43358
43359     getResizeEl : function(){
43360         return this.wrap;
43361     },
43362
43363     getPositionEl : function(){
43364         return this.wrap;
43365     },
43366
43367     // private
43368     onRender : function(ct, position){
43369         
43370         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
43371         //if(this.inputValue !== undefined){
43372         this.wrap = this.el.wrap();
43373         
43374         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
43375         
43376         if (this.bodyStyle) {
43377             this.viewEl.applyStyles(this.bodyStyle);
43378         }
43379         //this.viewEl.setStyle('padding', '2px');
43380         
43381         this.setValue(this.value);
43382         
43383     },
43384 /*
43385     // private
43386     initValue : Roo.emptyFn,
43387
43388   */
43389
43390         // private
43391     onClick : function(){
43392         
43393     },
43394
43395     /**
43396      * Sets the checked state of the checkbox.
43397      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
43398      */
43399     setValue : function(v){
43400         this.value = v;
43401         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
43402         // this might be called before we have a dom element..
43403         if (!this.viewEl) {
43404             return;
43405         }
43406         this.viewEl.dom.innerHTML = html;
43407         Roo.form.DisplayField.superclass.setValue.call(this, v);
43408
43409     }
43410 });/*
43411  * 
43412  * Licence- LGPL
43413  * 
43414  */
43415
43416 /**
43417  * @class Roo.form.DayPicker
43418  * @extends Roo.form.Field
43419  * A Day picker show [M] [T] [W] ....
43420  * @constructor
43421  * Creates a new Day Picker
43422  * @param {Object} config Configuration options
43423  */
43424 Roo.form.DayPicker= function(config){
43425     Roo.form.DayPicker.superclass.constructor.call(this, config);
43426      
43427 };
43428
43429 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
43430     /**
43431      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
43432      */
43433     focusClass : undefined,
43434     /**
43435      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
43436      */
43437     fieldClass: "x-form-field",
43438    
43439     /**
43440      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
43441      * {tag: "input", type: "checkbox", autocomplete: "off"})
43442      */
43443     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
43444     
43445    
43446     actionMode : 'viewEl', 
43447     //
43448     // private
43449  
43450     inputType : 'hidden',
43451     
43452      
43453     inputElement: false, // real input element?
43454     basedOn: false, // ????
43455     
43456     isFormField: true, // not sure where this is needed!!!!
43457
43458     onResize : function(){
43459         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
43460         if(!this.boxLabel){
43461             this.el.alignTo(this.wrap, 'c-c');
43462         }
43463     },
43464
43465     initEvents : function(){
43466         Roo.form.Checkbox.superclass.initEvents.call(this);
43467         this.el.on("click", this.onClick,  this);
43468         this.el.on("change", this.onClick,  this);
43469     },
43470
43471
43472     getResizeEl : function(){
43473         return this.wrap;
43474     },
43475
43476     getPositionEl : function(){
43477         return this.wrap;
43478     },
43479
43480     
43481     // private
43482     onRender : function(ct, position){
43483         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
43484        
43485         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
43486         
43487         var r1 = '<table><tr>';
43488         var r2 = '<tr class="x-form-daypick-icons">';
43489         for (var i=0; i < 7; i++) {
43490             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
43491             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
43492         }
43493         
43494         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
43495         viewEl.select('img').on('click', this.onClick, this);
43496         this.viewEl = viewEl;   
43497         
43498         
43499         // this will not work on Chrome!!!
43500         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
43501         this.el.on('propertychange', this.setFromHidden,  this);  //ie
43502         
43503         
43504           
43505
43506     },
43507
43508     // private
43509     initValue : Roo.emptyFn,
43510
43511     /**
43512      * Returns the checked state of the checkbox.
43513      * @return {Boolean} True if checked, else false
43514      */
43515     getValue : function(){
43516         return this.el.dom.value;
43517         
43518     },
43519
43520         // private
43521     onClick : function(e){ 
43522         //this.setChecked(!this.checked);
43523         Roo.get(e.target).toggleClass('x-menu-item-checked');
43524         this.refreshValue();
43525         //if(this.el.dom.checked != this.checked){
43526         //    this.setValue(this.el.dom.checked);
43527        // }
43528     },
43529     
43530     // private
43531     refreshValue : function()
43532     {
43533         var val = '';
43534         this.viewEl.select('img',true).each(function(e,i,n)  {
43535             val += e.is(".x-menu-item-checked") ? String(n) : '';
43536         });
43537         this.setValue(val, true);
43538     },
43539
43540     /**
43541      * Sets the checked state of the checkbox.
43542      * On is always based on a string comparison between inputValue and the param.
43543      * @param {Boolean/String} value - the value to set 
43544      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
43545      */
43546     setValue : function(v,suppressEvent){
43547         if (!this.el.dom) {
43548             return;
43549         }
43550         var old = this.el.dom.value ;
43551         this.el.dom.value = v;
43552         if (suppressEvent) {
43553             return ;
43554         }
43555          
43556         // update display..
43557         this.viewEl.select('img',true).each(function(e,i,n)  {
43558             
43559             var on = e.is(".x-menu-item-checked");
43560             var newv = v.indexOf(String(n)) > -1;
43561             if (on != newv) {
43562                 e.toggleClass('x-menu-item-checked');
43563             }
43564             
43565         });
43566         
43567         
43568         this.fireEvent('change', this, v, old);
43569         
43570         
43571     },
43572    
43573     // handle setting of hidden value by some other method!!?!?
43574     setFromHidden: function()
43575     {
43576         if(!this.el){
43577             return;
43578         }
43579         //console.log("SET FROM HIDDEN");
43580         //alert('setFrom hidden');
43581         this.setValue(this.el.dom.value);
43582     },
43583     
43584     onDestroy : function()
43585     {
43586         if(this.viewEl){
43587             Roo.get(this.viewEl).remove();
43588         }
43589          
43590         Roo.form.DayPicker.superclass.onDestroy.call(this);
43591     }
43592
43593 });/*
43594  * RooJS Library 1.1.1
43595  * Copyright(c) 2008-2011  Alan Knowles
43596  *
43597  * License - LGPL
43598  */
43599  
43600
43601 /**
43602  * @class Roo.form.ComboCheck
43603  * @extends Roo.form.ComboBox
43604  * A combobox for multiple select items.
43605  *
43606  * FIXME - could do with a reset button..
43607  * 
43608  * @constructor
43609  * Create a new ComboCheck
43610  * @param {Object} config Configuration options
43611  */
43612 Roo.form.ComboCheck = function(config){
43613     Roo.form.ComboCheck.superclass.constructor.call(this, config);
43614     // should verify some data...
43615     // like
43616     // hiddenName = required..
43617     // displayField = required
43618     // valudField == required
43619     var req= [ 'hiddenName', 'displayField', 'valueField' ];
43620     var _t = this;
43621     Roo.each(req, function(e) {
43622         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
43623             throw "Roo.form.ComboCheck : missing value for: " + e;
43624         }
43625     });
43626     
43627     
43628 };
43629
43630 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
43631      
43632      
43633     editable : false,
43634      
43635     selectedClass: 'x-menu-item-checked', 
43636     
43637     // private
43638     onRender : function(ct, position){
43639         var _t = this;
43640         
43641         
43642         
43643         if(!this.tpl){
43644             var cls = 'x-combo-list';
43645
43646             
43647             this.tpl =  new Roo.Template({
43648                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
43649                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
43650                    '<span>{' + this.displayField + '}</span>' +
43651                     '</div>' 
43652                 
43653             });
43654         }
43655  
43656         
43657         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
43658         this.view.singleSelect = false;
43659         this.view.multiSelect = true;
43660         this.view.toggleSelect = true;
43661         this.pageTb.add(new Roo.Toolbar.Fill(), {
43662             
43663             text: 'Done',
43664             handler: function()
43665             {
43666                 _t.collapse();
43667             }
43668         });
43669     },
43670     
43671     onViewOver : function(e, t){
43672         // do nothing...
43673         return;
43674         
43675     },
43676     
43677     onViewClick : function(doFocus,index){
43678         return;
43679         
43680     },
43681     select: function () {
43682         //Roo.log("SELECT CALLED");
43683     },
43684      
43685     selectByValue : function(xv, scrollIntoView){
43686         var ar = this.getValueArray();
43687         var sels = [];
43688         
43689         Roo.each(ar, function(v) {
43690             if(v === undefined || v === null){
43691                 return;
43692             }
43693             var r = this.findRecord(this.valueField, v);
43694             if(r){
43695                 sels.push(this.store.indexOf(r))
43696                 
43697             }
43698         },this);
43699         this.view.select(sels);
43700         return false;
43701     },
43702     
43703     
43704     
43705     onSelect : function(record, index){
43706        // Roo.log("onselect Called");
43707        // this is only called by the clear button now..
43708         this.view.clearSelections();
43709         this.setValue('[]');
43710         if (this.value != this.valueBefore) {
43711             this.fireEvent('change', this, this.value, this.valueBefore);
43712         }
43713     },
43714     getValueArray : function()
43715     {
43716         var ar = [] ;
43717         
43718         try {
43719             Roo.log(this.value);
43720             var ar = Roo.decode(this.value);
43721             return  ar instanceof Array ? ar : []; //?? valid?
43722             
43723         } catch(e) {
43724             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
43725             return [];
43726         }
43727          
43728     },
43729     expand : function ()
43730     {
43731         Roo.form.ComboCheck.superclass.expand.call(this);
43732         this.valueBefore = this.value;
43733         
43734
43735     },
43736     
43737     collapse : function(){
43738         Roo.form.ComboCheck.superclass.collapse.call(this);
43739         var sl = this.view.getSelectedIndexes();
43740         var st = this.store;
43741         var nv = [];
43742         var tv = [];
43743         var r;
43744         Roo.each(sl, function(i) {
43745             r = st.getAt(i);
43746             nv.push(r.get(this.valueField));
43747         },this);
43748         this.setValue(Roo.encode(nv));
43749         if (this.value != this.valueBefore) {
43750
43751             this.fireEvent('change', this, this.value, this.valueBefore);
43752         }
43753         
43754     },
43755     
43756     setValue : function(v){
43757         // Roo.log(v);
43758         this.value = v;
43759         
43760         var vals = this.getValueArray();
43761         var tv = [];
43762         Roo.each(vals, function(k) {
43763             var r = this.findRecord(this.valueField, k);
43764             if(r){
43765                 tv.push(r.data[this.displayField]);
43766             }else if(this.valueNotFoundText !== undefined){
43767                 tv.push( this.valueNotFoundText );
43768             }
43769         },this);
43770        // Roo.log(tv);
43771         
43772         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
43773         this.hiddenField.value = v;
43774         this.value = v;
43775     }
43776     
43777 });//<script type="text/javasscript">
43778  
43779
43780 /**
43781  * @class Roo.DDView
43782  * A DnD enabled version of Roo.View.
43783  * @param {Element/String} container The Element in which to create the View.
43784  * @param {String} tpl The template string used to create the markup for each element of the View
43785  * @param {Object} config The configuration properties. These include all the config options of
43786  * {@link Roo.View} plus some specific to this class.<br>
43787  * <p>
43788  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
43789  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
43790  * <p>
43791  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
43792 .x-view-drag-insert-above {
43793         border-top:1px dotted #3366cc;
43794 }
43795 .x-view-drag-insert-below {
43796         border-bottom:1px dotted #3366cc;
43797 }
43798 </code></pre>
43799  * 
43800  */
43801  
43802 Roo.DDView = function(container, tpl, config) {
43803     Roo.DDView.superclass.constructor.apply(this, arguments);
43804     this.getEl().setStyle("outline", "0px none");
43805     this.getEl().unselectable();
43806     if (this.dragGroup) {
43807                 this.setDraggable(this.dragGroup.split(","));
43808     }
43809     if (this.dropGroup) {
43810                 this.setDroppable(this.dropGroup.split(","));
43811     }
43812     if (this.deletable) {
43813         this.setDeletable();
43814     }
43815     this.isDirtyFlag = false;
43816         this.addEvents({
43817                 "drop" : true
43818         });
43819 };
43820
43821 Roo.extend(Roo.DDView, Roo.View, {
43822 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
43823 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
43824 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
43825 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
43826
43827         isFormField: true,
43828
43829         reset: Roo.emptyFn,
43830         
43831         clearInvalid: Roo.form.Field.prototype.clearInvalid,
43832
43833         validate: function() {
43834                 return true;
43835         },
43836         
43837         destroy: function() {
43838                 this.purgeListeners();
43839                 this.getEl.removeAllListeners();
43840                 this.getEl().remove();
43841                 if (this.dragZone) {
43842                         if (this.dragZone.destroy) {
43843                                 this.dragZone.destroy();
43844                         }
43845                 }
43846                 if (this.dropZone) {
43847                         if (this.dropZone.destroy) {
43848                                 this.dropZone.destroy();
43849                         }
43850                 }
43851         },
43852
43853 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
43854         getName: function() {
43855                 return this.name;
43856         },
43857
43858 /**     Loads the View from a JSON string representing the Records to put into the Store. */
43859         setValue: function(v) {
43860                 if (!this.store) {
43861                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
43862                 }
43863                 var data = {};
43864                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
43865                 this.store.proxy = new Roo.data.MemoryProxy(data);
43866                 this.store.load();
43867         },
43868
43869 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
43870         getValue: function() {
43871                 var result = '(';
43872                 this.store.each(function(rec) {
43873                         result += rec.id + ',';
43874                 });
43875                 return result.substr(0, result.length - 1) + ')';
43876         },
43877         
43878         getIds: function() {
43879                 var i = 0, result = new Array(this.store.getCount());
43880                 this.store.each(function(rec) {
43881                         result[i++] = rec.id;
43882                 });
43883                 return result;
43884         },
43885         
43886         isDirty: function() {
43887                 return this.isDirtyFlag;
43888         },
43889
43890 /**
43891  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
43892  *      whole Element becomes the target, and this causes the drop gesture to append.
43893  */
43894     getTargetFromEvent : function(e) {
43895                 var target = e.getTarget();
43896                 while ((target !== null) && (target.parentNode != this.el.dom)) {
43897                 target = target.parentNode;
43898                 }
43899                 if (!target) {
43900                         target = this.el.dom.lastChild || this.el.dom;
43901                 }
43902                 return target;
43903     },
43904
43905 /**
43906  *      Create the drag data which consists of an object which has the property "ddel" as
43907  *      the drag proxy element. 
43908  */
43909     getDragData : function(e) {
43910         var target = this.findItemFromChild(e.getTarget());
43911                 if(target) {
43912                         this.handleSelection(e);
43913                         var selNodes = this.getSelectedNodes();
43914             var dragData = {
43915                 source: this,
43916                 copy: this.copy || (this.allowCopy && e.ctrlKey),
43917                 nodes: selNodes,
43918                 records: []
43919                         };
43920                         var selectedIndices = this.getSelectedIndexes();
43921                         for (var i = 0; i < selectedIndices.length; i++) {
43922                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
43923                         }
43924                         if (selNodes.length == 1) {
43925                                 dragData.ddel = target.cloneNode(true); // the div element
43926                         } else {
43927                                 var div = document.createElement('div'); // create the multi element drag "ghost"
43928                                 div.className = 'multi-proxy';
43929                                 for (var i = 0, len = selNodes.length; i < len; i++) {
43930                                         div.appendChild(selNodes[i].cloneNode(true));
43931                                 }
43932                                 dragData.ddel = div;
43933                         }
43934             //console.log(dragData)
43935             //console.log(dragData.ddel.innerHTML)
43936                         return dragData;
43937                 }
43938         //console.log('nodragData')
43939                 return false;
43940     },
43941     
43942 /**     Specify to which ddGroup items in this DDView may be dragged. */
43943     setDraggable: function(ddGroup) {
43944         if (ddGroup instanceof Array) {
43945                 Roo.each(ddGroup, this.setDraggable, this);
43946                 return;
43947         }
43948         if (this.dragZone) {
43949                 this.dragZone.addToGroup(ddGroup);
43950         } else {
43951                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
43952                                 containerScroll: true,
43953                                 ddGroup: ddGroup 
43954
43955                         });
43956 //                      Draggability implies selection. DragZone's mousedown selects the element.
43957                         if (!this.multiSelect) { this.singleSelect = true; }
43958
43959 //                      Wire the DragZone's handlers up to methods in *this*
43960                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
43961                 }
43962     },
43963
43964 /**     Specify from which ddGroup this DDView accepts drops. */
43965     setDroppable: function(ddGroup) {
43966         if (ddGroup instanceof Array) {
43967                 Roo.each(ddGroup, this.setDroppable, this);
43968                 return;
43969         }
43970         if (this.dropZone) {
43971                 this.dropZone.addToGroup(ddGroup);
43972         } else {
43973                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
43974                                 containerScroll: true,
43975                                 ddGroup: ddGroup
43976                         });
43977
43978 //                      Wire the DropZone's handlers up to methods in *this*
43979                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
43980                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
43981                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
43982                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
43983                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
43984                 }
43985     },
43986
43987 /**     Decide whether to drop above or below a View node. */
43988     getDropPoint : function(e, n, dd){
43989         if (n == this.el.dom) { return "above"; }
43990                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
43991                 var c = t + (b - t) / 2;
43992                 var y = Roo.lib.Event.getPageY(e);
43993                 if(y <= c) {
43994                         return "above";
43995                 }else{
43996                         return "below";
43997                 }
43998     },
43999
44000     onNodeEnter : function(n, dd, e, data){
44001                 return false;
44002     },
44003     
44004     onNodeOver : function(n, dd, e, data){
44005                 var pt = this.getDropPoint(e, n, dd);
44006                 // set the insert point style on the target node
44007                 var dragElClass = this.dropNotAllowed;
44008                 if (pt) {
44009                         var targetElClass;
44010                         if (pt == "above"){
44011                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
44012                                 targetElClass = "x-view-drag-insert-above";
44013                         } else {
44014                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
44015                                 targetElClass = "x-view-drag-insert-below";
44016                         }
44017                         if (this.lastInsertClass != targetElClass){
44018                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
44019                                 this.lastInsertClass = targetElClass;
44020                         }
44021                 }
44022                 return dragElClass;
44023         },
44024
44025     onNodeOut : function(n, dd, e, data){
44026                 this.removeDropIndicators(n);
44027     },
44028
44029     onNodeDrop : function(n, dd, e, data){
44030         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
44031                 return false;
44032         }
44033         var pt = this.getDropPoint(e, n, dd);
44034                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
44035                 if (pt == "below") { insertAt++; }
44036                 for (var i = 0; i < data.records.length; i++) {
44037                         var r = data.records[i];
44038                         var dup = this.store.getById(r.id);
44039                         if (dup && (dd != this.dragZone)) {
44040                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
44041                         } else {
44042                                 if (data.copy) {
44043                                         this.store.insert(insertAt++, r.copy());
44044                                 } else {
44045                                         data.source.isDirtyFlag = true;
44046                                         r.store.remove(r);
44047                                         this.store.insert(insertAt++, r);
44048                                 }
44049                                 this.isDirtyFlag = true;
44050                         }
44051                 }
44052                 this.dragZone.cachedTarget = null;
44053                 return true;
44054     },
44055
44056     removeDropIndicators : function(n){
44057                 if(n){
44058                         Roo.fly(n).removeClass([
44059                                 "x-view-drag-insert-above",
44060                                 "x-view-drag-insert-below"]);
44061                         this.lastInsertClass = "_noclass";
44062                 }
44063     },
44064
44065 /**
44066  *      Utility method. Add a delete option to the DDView's context menu.
44067  *      @param {String} imageUrl The URL of the "delete" icon image.
44068  */
44069         setDeletable: function(imageUrl) {
44070                 if (!this.singleSelect && !this.multiSelect) {
44071                         this.singleSelect = true;
44072                 }
44073                 var c = this.getContextMenu();
44074                 this.contextMenu.on("itemclick", function(item) {
44075                         switch (item.id) {
44076                                 case "delete":
44077                                         this.remove(this.getSelectedIndexes());
44078                                         break;
44079                         }
44080                 }, this);
44081                 this.contextMenu.add({
44082                         icon: imageUrl,
44083                         id: "delete",
44084                         text: 'Delete'
44085                 });
44086         },
44087         
44088 /**     Return the context menu for this DDView. */
44089         getContextMenu: function() {
44090                 if (!this.contextMenu) {
44091 //                      Create the View's context menu
44092                         this.contextMenu = new Roo.menu.Menu({
44093                                 id: this.id + "-contextmenu"
44094                         });
44095                         this.el.on("contextmenu", this.showContextMenu, this);
44096                 }
44097                 return this.contextMenu;
44098         },
44099         
44100         disableContextMenu: function() {
44101                 if (this.contextMenu) {
44102                         this.el.un("contextmenu", this.showContextMenu, this);
44103                 }
44104         },
44105
44106         showContextMenu: function(e, item) {
44107         item = this.findItemFromChild(e.getTarget());
44108                 if (item) {
44109                         e.stopEvent();
44110                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
44111                         this.contextMenu.showAt(e.getXY());
44112             }
44113     },
44114
44115 /**
44116  *      Remove {@link Roo.data.Record}s at the specified indices.
44117  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
44118  */
44119     remove: function(selectedIndices) {
44120                 selectedIndices = [].concat(selectedIndices);
44121                 for (var i = 0; i < selectedIndices.length; i++) {
44122                         var rec = this.store.getAt(selectedIndices[i]);
44123                         this.store.remove(rec);
44124                 }
44125     },
44126
44127 /**
44128  *      Double click fires the event, but also, if this is draggable, and there is only one other
44129  *      related DropZone, it transfers the selected node.
44130  */
44131     onDblClick : function(e){
44132         var item = this.findItemFromChild(e.getTarget());
44133         if(item){
44134             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
44135                 return false;
44136             }
44137             if (this.dragGroup) {
44138                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
44139                     while (targets.indexOf(this.dropZone) > -1) {
44140                             targets.remove(this.dropZone);
44141                                 }
44142                     if (targets.length == 1) {
44143                                         this.dragZone.cachedTarget = null;
44144                         var el = Roo.get(targets[0].getEl());
44145                         var box = el.getBox(true);
44146                         targets[0].onNodeDrop(el.dom, {
44147                                 target: el.dom,
44148                                 xy: [box.x, box.y + box.height - 1]
44149                         }, null, this.getDragData(e));
44150                     }
44151                 }
44152         }
44153     },
44154     
44155     handleSelection: function(e) {
44156                 this.dragZone.cachedTarget = null;
44157         var item = this.findItemFromChild(e.getTarget());
44158         if (!item) {
44159                 this.clearSelections(true);
44160                 return;
44161         }
44162                 if (item && (this.multiSelect || this.singleSelect)){
44163                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
44164                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
44165                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
44166                                 this.unselect(item);
44167                         } else {
44168                                 this.select(item, this.multiSelect && e.ctrlKey);
44169                                 this.lastSelection = item;
44170                         }
44171                 }
44172     },
44173
44174     onItemClick : function(item, index, e){
44175                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
44176                         return false;
44177                 }
44178                 return true;
44179     },
44180
44181     unselect : function(nodeInfo, suppressEvent){
44182                 var node = this.getNode(nodeInfo);
44183                 if(node && this.isSelected(node)){
44184                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
44185                                 Roo.fly(node).removeClass(this.selectedClass);
44186                                 this.selections.remove(node);
44187                                 if(!suppressEvent){
44188                                         this.fireEvent("selectionchange", this, this.selections);
44189                                 }
44190                         }
44191                 }
44192     }
44193 });
44194 /*
44195  * Based on:
44196  * Ext JS Library 1.1.1
44197  * Copyright(c) 2006-2007, Ext JS, LLC.
44198  *
44199  * Originally Released Under LGPL - original licence link has changed is not relivant.
44200  *
44201  * Fork - LGPL
44202  * <script type="text/javascript">
44203  */
44204  
44205 /**
44206  * @class Roo.LayoutManager
44207  * @extends Roo.util.Observable
44208  * Base class for layout managers.
44209  */
44210 Roo.LayoutManager = function(container, config){
44211     Roo.LayoutManager.superclass.constructor.call(this);
44212     this.el = Roo.get(container);
44213     // ie scrollbar fix
44214     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
44215         document.body.scroll = "no";
44216     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
44217         this.el.position('relative');
44218     }
44219     this.id = this.el.id;
44220     this.el.addClass("x-layout-container");
44221     /** false to disable window resize monitoring @type Boolean */
44222     this.monitorWindowResize = true;
44223     this.regions = {};
44224     this.addEvents({
44225         /**
44226          * @event layout
44227          * Fires when a layout is performed. 
44228          * @param {Roo.LayoutManager} this
44229          */
44230         "layout" : true,
44231         /**
44232          * @event regionresized
44233          * Fires when the user resizes a region. 
44234          * @param {Roo.LayoutRegion} region The resized region
44235          * @param {Number} newSize The new size (width for east/west, height for north/south)
44236          */
44237         "regionresized" : true,
44238         /**
44239          * @event regioncollapsed
44240          * Fires when a region is collapsed. 
44241          * @param {Roo.LayoutRegion} region The collapsed region
44242          */
44243         "regioncollapsed" : true,
44244         /**
44245          * @event regionexpanded
44246          * Fires when a region is expanded.  
44247          * @param {Roo.LayoutRegion} region The expanded region
44248          */
44249         "regionexpanded" : true
44250     });
44251     this.updating = false;
44252     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
44253 };
44254
44255 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
44256     /**
44257      * Returns true if this layout is currently being updated
44258      * @return {Boolean}
44259      */
44260     isUpdating : function(){
44261         return this.updating; 
44262     },
44263     
44264     /**
44265      * Suspend the LayoutManager from doing auto-layouts while
44266      * making multiple add or remove calls
44267      */
44268     beginUpdate : function(){
44269         this.updating = true;    
44270     },
44271     
44272     /**
44273      * Restore auto-layouts and optionally disable the manager from performing a layout
44274      * @param {Boolean} noLayout true to disable a layout update 
44275      */
44276     endUpdate : function(noLayout){
44277         this.updating = false;
44278         if(!noLayout){
44279             this.layout();
44280         }    
44281     },
44282     
44283     layout: function(){
44284         
44285     },
44286     
44287     onRegionResized : function(region, newSize){
44288         this.fireEvent("regionresized", region, newSize);
44289         this.layout();
44290     },
44291     
44292     onRegionCollapsed : function(region){
44293         this.fireEvent("regioncollapsed", region);
44294     },
44295     
44296     onRegionExpanded : function(region){
44297         this.fireEvent("regionexpanded", region);
44298     },
44299         
44300     /**
44301      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
44302      * performs box-model adjustments.
44303      * @return {Object} The size as an object {width: (the width), height: (the height)}
44304      */
44305     getViewSize : function(){
44306         var size;
44307         if(this.el.dom != document.body){
44308             size = this.el.getSize();
44309         }else{
44310             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
44311         }
44312         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
44313         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
44314         return size;
44315     },
44316     
44317     /**
44318      * Returns the Element this layout is bound to.
44319      * @return {Roo.Element}
44320      */
44321     getEl : function(){
44322         return this.el;
44323     },
44324     
44325     /**
44326      * Returns the specified region.
44327      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
44328      * @return {Roo.LayoutRegion}
44329      */
44330     getRegion : function(target){
44331         return this.regions[target.toLowerCase()];
44332     },
44333     
44334     onWindowResize : function(){
44335         if(this.monitorWindowResize){
44336             this.layout();
44337         }
44338     }
44339 });/*
44340  * Based on:
44341  * Ext JS Library 1.1.1
44342  * Copyright(c) 2006-2007, Ext JS, LLC.
44343  *
44344  * Originally Released Under LGPL - original licence link has changed is not relivant.
44345  *
44346  * Fork - LGPL
44347  * <script type="text/javascript">
44348  */
44349 /**
44350  * @class Roo.BorderLayout
44351  * @extends Roo.LayoutManager
44352  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
44353  * please see: <br><br>
44354  * <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>
44355  * <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>
44356  * Example:
44357  <pre><code>
44358  var layout = new Roo.BorderLayout(document.body, {
44359     north: {
44360         initialSize: 25,
44361         titlebar: false
44362     },
44363     west: {
44364         split:true,
44365         initialSize: 200,
44366         minSize: 175,
44367         maxSize: 400,
44368         titlebar: true,
44369         collapsible: true
44370     },
44371     east: {
44372         split:true,
44373         initialSize: 202,
44374         minSize: 175,
44375         maxSize: 400,
44376         titlebar: true,
44377         collapsible: true
44378     },
44379     south: {
44380         split:true,
44381         initialSize: 100,
44382         minSize: 100,
44383         maxSize: 200,
44384         titlebar: true,
44385         collapsible: true
44386     },
44387     center: {
44388         titlebar: true,
44389         autoScroll:true,
44390         resizeTabs: true,
44391         minTabWidth: 50,
44392         preferredTabWidth: 150
44393     }
44394 });
44395
44396 // shorthand
44397 var CP = Roo.ContentPanel;
44398
44399 layout.beginUpdate();
44400 layout.add("north", new CP("north", "North"));
44401 layout.add("south", new CP("south", {title: "South", closable: true}));
44402 layout.add("west", new CP("west", {title: "West"}));
44403 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
44404 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
44405 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
44406 layout.getRegion("center").showPanel("center1");
44407 layout.endUpdate();
44408 </code></pre>
44409
44410 <b>The container the layout is rendered into can be either the body element or any other element.
44411 If it is not the body element, the container needs to either be an absolute positioned element,
44412 or you will need to add "position:relative" to the css of the container.  You will also need to specify
44413 the container size if it is not the body element.</b>
44414
44415 * @constructor
44416 * Create a new BorderLayout
44417 * @param {String/HTMLElement/Element} container The container this layout is bound to
44418 * @param {Object} config Configuration options
44419  */
44420 Roo.BorderLayout = function(container, config){
44421     config = config || {};
44422     Roo.BorderLayout.superclass.constructor.call(this, container, config);
44423     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
44424     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
44425         var target = this.factory.validRegions[i];
44426         if(config[target]){
44427             this.addRegion(target, config[target]);
44428         }
44429     }
44430 };
44431
44432 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
44433     /**
44434      * Creates and adds a new region if it doesn't already exist.
44435      * @param {String} target The target region key (north, south, east, west or center).
44436      * @param {Object} config The regions config object
44437      * @return {BorderLayoutRegion} The new region
44438      */
44439     addRegion : function(target, config){
44440         if(!this.regions[target]){
44441             var r = this.factory.create(target, this, config);
44442             this.bindRegion(target, r);
44443         }
44444         return this.regions[target];
44445     },
44446
44447     // private (kinda)
44448     bindRegion : function(name, r){
44449         this.regions[name] = r;
44450         r.on("visibilitychange", this.layout, this);
44451         r.on("paneladded", this.layout, this);
44452         r.on("panelremoved", this.layout, this);
44453         r.on("invalidated", this.layout, this);
44454         r.on("resized", this.onRegionResized, this);
44455         r.on("collapsed", this.onRegionCollapsed, this);
44456         r.on("expanded", this.onRegionExpanded, this);
44457     },
44458
44459     /**
44460      * Performs a layout update.
44461      */
44462     layout : function(){
44463         if(this.updating) return;
44464         var size = this.getViewSize();
44465         var w = size.width;
44466         var h = size.height;
44467         var centerW = w;
44468         var centerH = h;
44469         var centerY = 0;
44470         var centerX = 0;
44471         //var x = 0, y = 0;
44472
44473         var rs = this.regions;
44474         var north = rs["north"];
44475         var south = rs["south"]; 
44476         var west = rs["west"];
44477         var east = rs["east"];
44478         var center = rs["center"];
44479         //if(this.hideOnLayout){ // not supported anymore
44480             //c.el.setStyle("display", "none");
44481         //}
44482         if(north && north.isVisible()){
44483             var b = north.getBox();
44484             var m = north.getMargins();
44485             b.width = w - (m.left+m.right);
44486             b.x = m.left;
44487             b.y = m.top;
44488             centerY = b.height + b.y + m.bottom;
44489             centerH -= centerY;
44490             north.updateBox(this.safeBox(b));
44491         }
44492         if(south && south.isVisible()){
44493             var b = south.getBox();
44494             var m = south.getMargins();
44495             b.width = w - (m.left+m.right);
44496             b.x = m.left;
44497             var totalHeight = (b.height + m.top + m.bottom);
44498             b.y = h - totalHeight + m.top;
44499             centerH -= totalHeight;
44500             south.updateBox(this.safeBox(b));
44501         }
44502         if(west && west.isVisible()){
44503             var b = west.getBox();
44504             var m = west.getMargins();
44505             b.height = centerH - (m.top+m.bottom);
44506             b.x = m.left;
44507             b.y = centerY + m.top;
44508             var totalWidth = (b.width + m.left + m.right);
44509             centerX += totalWidth;
44510             centerW -= totalWidth;
44511             west.updateBox(this.safeBox(b));
44512         }
44513         if(east && east.isVisible()){
44514             var b = east.getBox();
44515             var m = east.getMargins();
44516             b.height = centerH - (m.top+m.bottom);
44517             var totalWidth = (b.width + m.left + m.right);
44518             b.x = w - totalWidth + m.left;
44519             b.y = centerY + m.top;
44520             centerW -= totalWidth;
44521             east.updateBox(this.safeBox(b));
44522         }
44523         if(center){
44524             var m = center.getMargins();
44525             var centerBox = {
44526                 x: centerX + m.left,
44527                 y: centerY + m.top,
44528                 width: centerW - (m.left+m.right),
44529                 height: centerH - (m.top+m.bottom)
44530             };
44531             //if(this.hideOnLayout){
44532                 //center.el.setStyle("display", "block");
44533             //}
44534             center.updateBox(this.safeBox(centerBox));
44535         }
44536         this.el.repaint();
44537         this.fireEvent("layout", this);
44538     },
44539
44540     // private
44541     safeBox : function(box){
44542         box.width = Math.max(0, box.width);
44543         box.height = Math.max(0, box.height);
44544         return box;
44545     },
44546
44547     /**
44548      * Adds a ContentPanel (or subclass) to this layout.
44549      * @param {String} target The target region key (north, south, east, west or center).
44550      * @param {Roo.ContentPanel} panel The panel to add
44551      * @return {Roo.ContentPanel} The added panel
44552      */
44553     add : function(target, panel){
44554          
44555         target = target.toLowerCase();
44556         return this.regions[target].add(panel);
44557     },
44558
44559     /**
44560      * Remove a ContentPanel (or subclass) to this layout.
44561      * @param {String} target The target region key (north, south, east, west or center).
44562      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
44563      * @return {Roo.ContentPanel} The removed panel
44564      */
44565     remove : function(target, panel){
44566         target = target.toLowerCase();
44567         return this.regions[target].remove(panel);
44568     },
44569
44570     /**
44571      * Searches all regions for a panel with the specified id
44572      * @param {String} panelId
44573      * @return {Roo.ContentPanel} The panel or null if it wasn't found
44574      */
44575     findPanel : function(panelId){
44576         var rs = this.regions;
44577         for(var target in rs){
44578             if(typeof rs[target] != "function"){
44579                 var p = rs[target].getPanel(panelId);
44580                 if(p){
44581                     return p;
44582                 }
44583             }
44584         }
44585         return null;
44586     },
44587
44588     /**
44589      * Searches all regions for a panel with the specified id and activates (shows) it.
44590      * @param {String/ContentPanel} panelId The panels id or the panel itself
44591      * @return {Roo.ContentPanel} The shown panel or null
44592      */
44593     showPanel : function(panelId) {
44594       var rs = this.regions;
44595       for(var target in rs){
44596          var r = rs[target];
44597          if(typeof r != "function"){
44598             if(r.hasPanel(panelId)){
44599                return r.showPanel(panelId);
44600             }
44601          }
44602       }
44603       return null;
44604    },
44605
44606    /**
44607      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
44608      * @param {Roo.state.Provider} provider (optional) An alternate state provider
44609      */
44610     restoreState : function(provider){
44611         if(!provider){
44612             provider = Roo.state.Manager;
44613         }
44614         var sm = new Roo.LayoutStateManager();
44615         sm.init(this, provider);
44616     },
44617
44618     /**
44619      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
44620      * object should contain properties for each region to add ContentPanels to, and each property's value should be
44621      * a valid ContentPanel config object.  Example:
44622      * <pre><code>
44623 // Create the main layout
44624 var layout = new Roo.BorderLayout('main-ct', {
44625     west: {
44626         split:true,
44627         minSize: 175,
44628         titlebar: true
44629     },
44630     center: {
44631         title:'Components'
44632     }
44633 }, 'main-ct');
44634
44635 // Create and add multiple ContentPanels at once via configs
44636 layout.batchAdd({
44637    west: {
44638        id: 'source-files',
44639        autoCreate:true,
44640        title:'Ext Source Files',
44641        autoScroll:true,
44642        fitToFrame:true
44643    },
44644    center : {
44645        el: cview,
44646        autoScroll:true,
44647        fitToFrame:true,
44648        toolbar: tb,
44649        resizeEl:'cbody'
44650    }
44651 });
44652 </code></pre>
44653      * @param {Object} regions An object containing ContentPanel configs by region name
44654      */
44655     batchAdd : function(regions){
44656         this.beginUpdate();
44657         for(var rname in regions){
44658             var lr = this.regions[rname];
44659             if(lr){
44660                 this.addTypedPanels(lr, regions[rname]);
44661             }
44662         }
44663         this.endUpdate();
44664     },
44665
44666     // private
44667     addTypedPanels : function(lr, ps){
44668         if(typeof ps == 'string'){
44669             lr.add(new Roo.ContentPanel(ps));
44670         }
44671         else if(ps instanceof Array){
44672             for(var i =0, len = ps.length; i < len; i++){
44673                 this.addTypedPanels(lr, ps[i]);
44674             }
44675         }
44676         else if(!ps.events){ // raw config?
44677             var el = ps.el;
44678             delete ps.el; // prevent conflict
44679             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
44680         }
44681         else {  // panel object assumed!
44682             lr.add(ps);
44683         }
44684     },
44685     /**
44686      * Adds a xtype elements to the layout.
44687      * <pre><code>
44688
44689 layout.addxtype({
44690        xtype : 'ContentPanel',
44691        region: 'west',
44692        items: [ .... ]
44693    }
44694 );
44695
44696 layout.addxtype({
44697         xtype : 'NestedLayoutPanel',
44698         region: 'west',
44699         layout: {
44700            center: { },
44701            west: { }   
44702         },
44703         items : [ ... list of content panels or nested layout panels.. ]
44704    }
44705 );
44706 </code></pre>
44707      * @param {Object} cfg Xtype definition of item to add.
44708      */
44709     addxtype : function(cfg)
44710     {
44711         // basically accepts a pannel...
44712         // can accept a layout region..!?!?
44713         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
44714         
44715         if (!cfg.xtype.match(/Panel$/)) {
44716             return false;
44717         }
44718         var ret = false;
44719         
44720         if (typeof(cfg.region) == 'undefined') {
44721             Roo.log("Failed to add Panel, region was not set");
44722             Roo.log(cfg);
44723             return false;
44724         }
44725         var region = cfg.region;
44726         delete cfg.region;
44727         
44728           
44729         var xitems = [];
44730         if (cfg.items) {
44731             xitems = cfg.items;
44732             delete cfg.items;
44733         }
44734         
44735         
44736         switch(cfg.xtype) 
44737         {
44738             case 'ContentPanel':  // ContentPanel (el, cfg)
44739             case 'ScrollPanel':  // ContentPanel (el, cfg)
44740                 if(cfg.autoCreate) {
44741                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
44742                 } else {
44743                     var el = this.el.createChild();
44744                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
44745                 }
44746                 
44747                 this.add(region, ret);
44748                 break;
44749             
44750             
44751             case 'TreePanel': // our new panel!
44752                 cfg.el = this.el.createChild();
44753                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
44754                 this.add(region, ret);
44755                 break;
44756             
44757             case 'NestedLayoutPanel': 
44758                 // create a new Layout (which is  a Border Layout...
44759                 var el = this.el.createChild();
44760                 var clayout = cfg.layout;
44761                 delete cfg.layout;
44762                 clayout.items   = clayout.items  || [];
44763                 // replace this exitems with the clayout ones..
44764                 xitems = clayout.items;
44765                  
44766                 
44767                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
44768                     cfg.background = false;
44769                 }
44770                 var layout = new Roo.BorderLayout(el, clayout);
44771                 
44772                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
44773                 //console.log('adding nested layout panel '  + cfg.toSource());
44774                 this.add(region, ret);
44775                 
44776                 break;
44777                 
44778             case 'GridPanel': 
44779             
44780                 // needs grid and region
44781                 
44782                 //var el = this.getRegion(region).el.createChild();
44783                 var el = this.el.createChild();
44784                 // create the grid first...
44785                 
44786                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
44787                 delete cfg.grid;
44788                 if (region == 'center' && this.active ) {
44789                     cfg.background = false;
44790                 }
44791                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
44792                 
44793                 this.add(region, ret);
44794                 if (cfg.background) {
44795                     ret.on('activate', function(gp) {
44796                         if (!gp.grid.rendered) {
44797                             gp.grid.render();
44798                         }
44799                     });
44800                 } else {
44801                     grid.render();
44802                 }
44803                 break;
44804            
44805                
44806                 
44807                 
44808             default: 
44809                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
44810                 return null;
44811              // GridPanel (grid, cfg)
44812             
44813         }
44814         this.beginUpdate();
44815         // add children..
44816         Roo.each(xitems, function(i)  {
44817             ret.addxtype(i);
44818         });
44819         this.endUpdate();
44820         return ret;
44821         
44822     }
44823 });
44824
44825 /**
44826  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
44827  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
44828  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
44829  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
44830  * <pre><code>
44831 // shorthand
44832 var CP = Roo.ContentPanel;
44833
44834 var layout = Roo.BorderLayout.create({
44835     north: {
44836         initialSize: 25,
44837         titlebar: false,
44838         panels: [new CP("north", "North")]
44839     },
44840     west: {
44841         split:true,
44842         initialSize: 200,
44843         minSize: 175,
44844         maxSize: 400,
44845         titlebar: true,
44846         collapsible: true,
44847         panels: [new CP("west", {title: "West"})]
44848     },
44849     east: {
44850         split:true,
44851         initialSize: 202,
44852         minSize: 175,
44853         maxSize: 400,
44854         titlebar: true,
44855         collapsible: true,
44856         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
44857     },
44858     south: {
44859         split:true,
44860         initialSize: 100,
44861         minSize: 100,
44862         maxSize: 200,
44863         titlebar: true,
44864         collapsible: true,
44865         panels: [new CP("south", {title: "South", closable: true})]
44866     },
44867     center: {
44868         titlebar: true,
44869         autoScroll:true,
44870         resizeTabs: true,
44871         minTabWidth: 50,
44872         preferredTabWidth: 150,
44873         panels: [
44874             new CP("center1", {title: "Close Me", closable: true}),
44875             new CP("center2", {title: "Center Panel", closable: false})
44876         ]
44877     }
44878 }, document.body);
44879
44880 layout.getRegion("center").showPanel("center1");
44881 </code></pre>
44882  * @param config
44883  * @param targetEl
44884  */
44885 Roo.BorderLayout.create = function(config, targetEl){
44886     var layout = new Roo.BorderLayout(targetEl || document.body, config);
44887     layout.beginUpdate();
44888     var regions = Roo.BorderLayout.RegionFactory.validRegions;
44889     for(var j = 0, jlen = regions.length; j < jlen; j++){
44890         var lr = regions[j];
44891         if(layout.regions[lr] && config[lr].panels){
44892             var r = layout.regions[lr];
44893             var ps = config[lr].panels;
44894             layout.addTypedPanels(r, ps);
44895         }
44896     }
44897     layout.endUpdate();
44898     return layout;
44899 };
44900
44901 // private
44902 Roo.BorderLayout.RegionFactory = {
44903     // private
44904     validRegions : ["north","south","east","west","center"],
44905
44906     // private
44907     create : function(target, mgr, config){
44908         target = target.toLowerCase();
44909         if(config.lightweight || config.basic){
44910             return new Roo.BasicLayoutRegion(mgr, config, target);
44911         }
44912         switch(target){
44913             case "north":
44914                 return new Roo.NorthLayoutRegion(mgr, config);
44915             case "south":
44916                 return new Roo.SouthLayoutRegion(mgr, config);
44917             case "east":
44918                 return new Roo.EastLayoutRegion(mgr, config);
44919             case "west":
44920                 return new Roo.WestLayoutRegion(mgr, config);
44921             case "center":
44922                 return new Roo.CenterLayoutRegion(mgr, config);
44923         }
44924         throw 'Layout region "'+target+'" not supported.';
44925     }
44926 };/*
44927  * Based on:
44928  * Ext JS Library 1.1.1
44929  * Copyright(c) 2006-2007, Ext JS, LLC.
44930  *
44931  * Originally Released Under LGPL - original licence link has changed is not relivant.
44932  *
44933  * Fork - LGPL
44934  * <script type="text/javascript">
44935  */
44936  
44937 /**
44938  * @class Roo.BasicLayoutRegion
44939  * @extends Roo.util.Observable
44940  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
44941  * and does not have a titlebar, tabs or any other features. All it does is size and position 
44942  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
44943  */
44944 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
44945     this.mgr = mgr;
44946     this.position  = pos;
44947     this.events = {
44948         /**
44949          * @scope Roo.BasicLayoutRegion
44950          */
44951         
44952         /**
44953          * @event beforeremove
44954          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
44955          * @param {Roo.LayoutRegion} this
44956          * @param {Roo.ContentPanel} panel The panel
44957          * @param {Object} e The cancel event object
44958          */
44959         "beforeremove" : true,
44960         /**
44961          * @event invalidated
44962          * Fires when the layout for this region is changed.
44963          * @param {Roo.LayoutRegion} this
44964          */
44965         "invalidated" : true,
44966         /**
44967          * @event visibilitychange
44968          * Fires when this region is shown or hidden 
44969          * @param {Roo.LayoutRegion} this
44970          * @param {Boolean} visibility true or false
44971          */
44972         "visibilitychange" : true,
44973         /**
44974          * @event paneladded
44975          * Fires when a panel is added. 
44976          * @param {Roo.LayoutRegion} this
44977          * @param {Roo.ContentPanel} panel The panel
44978          */
44979         "paneladded" : true,
44980         /**
44981          * @event panelremoved
44982          * Fires when a panel is removed. 
44983          * @param {Roo.LayoutRegion} this
44984          * @param {Roo.ContentPanel} panel The panel
44985          */
44986         "panelremoved" : true,
44987         /**
44988          * @event collapsed
44989          * Fires when this region is collapsed.
44990          * @param {Roo.LayoutRegion} this
44991          */
44992         "collapsed" : true,
44993         /**
44994          * @event expanded
44995          * Fires when this region is expanded.
44996          * @param {Roo.LayoutRegion} this
44997          */
44998         "expanded" : true,
44999         /**
45000          * @event slideshow
45001          * Fires when this region is slid into view.
45002          * @param {Roo.LayoutRegion} this
45003          */
45004         "slideshow" : true,
45005         /**
45006          * @event slidehide
45007          * Fires when this region slides out of view. 
45008          * @param {Roo.LayoutRegion} this
45009          */
45010         "slidehide" : true,
45011         /**
45012          * @event panelactivated
45013          * Fires when a panel is activated. 
45014          * @param {Roo.LayoutRegion} this
45015          * @param {Roo.ContentPanel} panel The activated panel
45016          */
45017         "panelactivated" : true,
45018         /**
45019          * @event resized
45020          * Fires when the user resizes this region. 
45021          * @param {Roo.LayoutRegion} this
45022          * @param {Number} newSize The new size (width for east/west, height for north/south)
45023          */
45024         "resized" : true
45025     };
45026     /** A collection of panels in this region. @type Roo.util.MixedCollection */
45027     this.panels = new Roo.util.MixedCollection();
45028     this.panels.getKey = this.getPanelId.createDelegate(this);
45029     this.box = null;
45030     this.activePanel = null;
45031     // ensure listeners are added...
45032     
45033     if (config.listeners || config.events) {
45034         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
45035             listeners : config.listeners || {},
45036             events : config.events || {}
45037         });
45038     }
45039     
45040     if(skipConfig !== true){
45041         this.applyConfig(config);
45042     }
45043 };
45044
45045 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
45046     getPanelId : function(p){
45047         return p.getId();
45048     },
45049     
45050     applyConfig : function(config){
45051         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
45052         this.config = config;
45053         
45054     },
45055     
45056     /**
45057      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
45058      * the width, for horizontal (north, south) the height.
45059      * @param {Number} newSize The new width or height
45060      */
45061     resizeTo : function(newSize){
45062         var el = this.el ? this.el :
45063                  (this.activePanel ? this.activePanel.getEl() : null);
45064         if(el){
45065             switch(this.position){
45066                 case "east":
45067                 case "west":
45068                     el.setWidth(newSize);
45069                     this.fireEvent("resized", this, newSize);
45070                 break;
45071                 case "north":
45072                 case "south":
45073                     el.setHeight(newSize);
45074                     this.fireEvent("resized", this, newSize);
45075                 break;                
45076             }
45077         }
45078     },
45079     
45080     getBox : function(){
45081         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
45082     },
45083     
45084     getMargins : function(){
45085         return this.margins;
45086     },
45087     
45088     updateBox : function(box){
45089         this.box = box;
45090         var el = this.activePanel.getEl();
45091         el.dom.style.left = box.x + "px";
45092         el.dom.style.top = box.y + "px";
45093         this.activePanel.setSize(box.width, box.height);
45094     },
45095     
45096     /**
45097      * Returns the container element for this region.
45098      * @return {Roo.Element}
45099      */
45100     getEl : function(){
45101         return this.activePanel;
45102     },
45103     
45104     /**
45105      * Returns true if this region is currently visible.
45106      * @return {Boolean}
45107      */
45108     isVisible : function(){
45109         return this.activePanel ? true : false;
45110     },
45111     
45112     setActivePanel : function(panel){
45113         panel = this.getPanel(panel);
45114         if(this.activePanel && this.activePanel != panel){
45115             this.activePanel.setActiveState(false);
45116             this.activePanel.getEl().setLeftTop(-10000,-10000);
45117         }
45118         this.activePanel = panel;
45119         panel.setActiveState(true);
45120         if(this.box){
45121             panel.setSize(this.box.width, this.box.height);
45122         }
45123         this.fireEvent("panelactivated", this, panel);
45124         this.fireEvent("invalidated");
45125     },
45126     
45127     /**
45128      * Show the specified panel.
45129      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
45130      * @return {Roo.ContentPanel} The shown panel or null
45131      */
45132     showPanel : function(panel){
45133         if(panel = this.getPanel(panel)){
45134             this.setActivePanel(panel);
45135         }
45136         return panel;
45137     },
45138     
45139     /**
45140      * Get the active panel for this region.
45141      * @return {Roo.ContentPanel} The active panel or null
45142      */
45143     getActivePanel : function(){
45144         return this.activePanel;
45145     },
45146     
45147     /**
45148      * Add the passed ContentPanel(s)
45149      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
45150      * @return {Roo.ContentPanel} The panel added (if only one was added)
45151      */
45152     add : function(panel){
45153         if(arguments.length > 1){
45154             for(var i = 0, len = arguments.length; i < len; i++) {
45155                 this.add(arguments[i]);
45156             }
45157             return null;
45158         }
45159         if(this.hasPanel(panel)){
45160             this.showPanel(panel);
45161             return panel;
45162         }
45163         var el = panel.getEl();
45164         if(el.dom.parentNode != this.mgr.el.dom){
45165             this.mgr.el.dom.appendChild(el.dom);
45166         }
45167         if(panel.setRegion){
45168             panel.setRegion(this);
45169         }
45170         this.panels.add(panel);
45171         el.setStyle("position", "absolute");
45172         if(!panel.background){
45173             this.setActivePanel(panel);
45174             if(this.config.initialSize && this.panels.getCount()==1){
45175                 this.resizeTo(this.config.initialSize);
45176             }
45177         }
45178         this.fireEvent("paneladded", this, panel);
45179         return panel;
45180     },
45181     
45182     /**
45183      * Returns true if the panel is in this region.
45184      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
45185      * @return {Boolean}
45186      */
45187     hasPanel : function(panel){
45188         if(typeof panel == "object"){ // must be panel obj
45189             panel = panel.getId();
45190         }
45191         return this.getPanel(panel) ? true : false;
45192     },
45193     
45194     /**
45195      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
45196      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
45197      * @param {Boolean} preservePanel Overrides the config preservePanel option
45198      * @return {Roo.ContentPanel} The panel that was removed
45199      */
45200     remove : function(panel, preservePanel){
45201         panel = this.getPanel(panel);
45202         if(!panel){
45203             return null;
45204         }
45205         var e = {};
45206         this.fireEvent("beforeremove", this, panel, e);
45207         if(e.cancel === true){
45208             return null;
45209         }
45210         var panelId = panel.getId();
45211         this.panels.removeKey(panelId);
45212         return panel;
45213     },
45214     
45215     /**
45216      * Returns the panel specified or null if it's not in this region.
45217      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
45218      * @return {Roo.ContentPanel}
45219      */
45220     getPanel : function(id){
45221         if(typeof id == "object"){ // must be panel obj
45222             return id;
45223         }
45224         return this.panels.get(id);
45225     },
45226     
45227     /**
45228      * Returns this regions position (north/south/east/west/center).
45229      * @return {String} 
45230      */
45231     getPosition: function(){
45232         return this.position;    
45233     }
45234 });/*
45235  * Based on:
45236  * Ext JS Library 1.1.1
45237  * Copyright(c) 2006-2007, Ext JS, LLC.
45238  *
45239  * Originally Released Under LGPL - original licence link has changed is not relivant.
45240  *
45241  * Fork - LGPL
45242  * <script type="text/javascript">
45243  */
45244  
45245 /**
45246  * @class Roo.LayoutRegion
45247  * @extends Roo.BasicLayoutRegion
45248  * This class represents a region in a layout manager.
45249  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
45250  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
45251  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
45252  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
45253  * @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})
45254  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
45255  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
45256  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
45257  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
45258  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
45259  * @cfg {String}    title           The title for the region (overrides panel titles)
45260  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
45261  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
45262  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
45263  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
45264  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
45265  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
45266  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
45267  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
45268  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
45269  * @cfg {Boolean}   showPin         True to show a pin button
45270  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
45271  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
45272  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
45273  * @cfg {Number}    width           For East/West panels
45274  * @cfg {Number}    height          For North/South panels
45275  * @cfg {Boolean}   split           To show the splitter
45276  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
45277  */
45278 Roo.LayoutRegion = function(mgr, config, pos){
45279     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
45280     var dh = Roo.DomHelper;
45281     /** This region's container element 
45282     * @type Roo.Element */
45283     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
45284     /** This region's title element 
45285     * @type Roo.Element */
45286
45287     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
45288         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
45289         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
45290     ]}, true);
45291     this.titleEl.enableDisplayMode();
45292     /** This region's title text element 
45293     * @type HTMLElement */
45294     this.titleTextEl = this.titleEl.dom.firstChild;
45295     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
45296     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
45297     this.closeBtn.enableDisplayMode();
45298     this.closeBtn.on("click", this.closeClicked, this);
45299     this.closeBtn.hide();
45300
45301     this.createBody(config);
45302     this.visible = true;
45303     this.collapsed = false;
45304
45305     if(config.hideWhenEmpty){
45306         this.hide();
45307         this.on("paneladded", this.validateVisibility, this);
45308         this.on("panelremoved", this.validateVisibility, this);
45309     }
45310     this.applyConfig(config);
45311 };
45312
45313 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
45314
45315     createBody : function(){
45316         /** This region's body element 
45317         * @type Roo.Element */
45318         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
45319     },
45320
45321     applyConfig : function(c){
45322         if(c.collapsible && this.position != "center" && !this.collapsedEl){
45323             var dh = Roo.DomHelper;
45324             if(c.titlebar !== false){
45325                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
45326                 this.collapseBtn.on("click", this.collapse, this);
45327                 this.collapseBtn.enableDisplayMode();
45328
45329                 if(c.showPin === true || this.showPin){
45330                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
45331                     this.stickBtn.enableDisplayMode();
45332                     this.stickBtn.on("click", this.expand, this);
45333                     this.stickBtn.hide();
45334                 }
45335             }
45336             /** This region's collapsed element
45337             * @type Roo.Element */
45338             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
45339                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
45340             ]}, true);
45341             if(c.floatable !== false){
45342                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
45343                this.collapsedEl.on("click", this.collapseClick, this);
45344             }
45345
45346             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
45347                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
45348                    id: "message", unselectable: "on", style:{"float":"left"}});
45349                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
45350              }
45351             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
45352             this.expandBtn.on("click", this.expand, this);
45353         }
45354         if(this.collapseBtn){
45355             this.collapseBtn.setVisible(c.collapsible == true);
45356         }
45357         this.cmargins = c.cmargins || this.cmargins ||
45358                          (this.position == "west" || this.position == "east" ?
45359                              {top: 0, left: 2, right:2, bottom: 0} :
45360                              {top: 2, left: 0, right:0, bottom: 2});
45361         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
45362         this.bottomTabs = c.tabPosition != "top";
45363         this.autoScroll = c.autoScroll || false;
45364         if(this.autoScroll){
45365             this.bodyEl.setStyle("overflow", "auto");
45366         }else{
45367             this.bodyEl.setStyle("overflow", "hidden");
45368         }
45369         //if(c.titlebar !== false){
45370             if((!c.titlebar && !c.title) || c.titlebar === false){
45371                 this.titleEl.hide();
45372             }else{
45373                 this.titleEl.show();
45374                 if(c.title){
45375                     this.titleTextEl.innerHTML = c.title;
45376                 }
45377             }
45378         //}
45379         this.duration = c.duration || .30;
45380         this.slideDuration = c.slideDuration || .45;
45381         this.config = c;
45382         if(c.collapsed){
45383             this.collapse(true);
45384         }
45385         if(c.hidden){
45386             this.hide();
45387         }
45388     },
45389     /**
45390      * Returns true if this region is currently visible.
45391      * @return {Boolean}
45392      */
45393     isVisible : function(){
45394         return this.visible;
45395     },
45396
45397     /**
45398      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
45399      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
45400      */
45401     setCollapsedTitle : function(title){
45402         title = title || "&#160;";
45403         if(this.collapsedTitleTextEl){
45404             this.collapsedTitleTextEl.innerHTML = title;
45405         }
45406     },
45407
45408     getBox : function(){
45409         var b;
45410         if(!this.collapsed){
45411             b = this.el.getBox(false, true);
45412         }else{
45413             b = this.collapsedEl.getBox(false, true);
45414         }
45415         return b;
45416     },
45417
45418     getMargins : function(){
45419         return this.collapsed ? this.cmargins : this.margins;
45420     },
45421
45422     highlight : function(){
45423         this.el.addClass("x-layout-panel-dragover");
45424     },
45425
45426     unhighlight : function(){
45427         this.el.removeClass("x-layout-panel-dragover");
45428     },
45429
45430     updateBox : function(box){
45431         this.box = box;
45432         if(!this.collapsed){
45433             this.el.dom.style.left = box.x + "px";
45434             this.el.dom.style.top = box.y + "px";
45435             this.updateBody(box.width, box.height);
45436         }else{
45437             this.collapsedEl.dom.style.left = box.x + "px";
45438             this.collapsedEl.dom.style.top = box.y + "px";
45439             this.collapsedEl.setSize(box.width, box.height);
45440         }
45441         if(this.tabs){
45442             this.tabs.autoSizeTabs();
45443         }
45444     },
45445
45446     updateBody : function(w, h){
45447         if(w !== null){
45448             this.el.setWidth(w);
45449             w -= this.el.getBorderWidth("rl");
45450             if(this.config.adjustments){
45451                 w += this.config.adjustments[0];
45452             }
45453         }
45454         if(h !== null){
45455             this.el.setHeight(h);
45456             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
45457             h -= this.el.getBorderWidth("tb");
45458             if(this.config.adjustments){
45459                 h += this.config.adjustments[1];
45460             }
45461             this.bodyEl.setHeight(h);
45462             if(this.tabs){
45463                 h = this.tabs.syncHeight(h);
45464             }
45465         }
45466         if(this.panelSize){
45467             w = w !== null ? w : this.panelSize.width;
45468             h = h !== null ? h : this.panelSize.height;
45469         }
45470         if(this.activePanel){
45471             var el = this.activePanel.getEl();
45472             w = w !== null ? w : el.getWidth();
45473             h = h !== null ? h : el.getHeight();
45474             this.panelSize = {width: w, height: h};
45475             this.activePanel.setSize(w, h);
45476         }
45477         if(Roo.isIE && this.tabs){
45478             this.tabs.el.repaint();
45479         }
45480     },
45481
45482     /**
45483      * Returns the container element for this region.
45484      * @return {Roo.Element}
45485      */
45486     getEl : function(){
45487         return this.el;
45488     },
45489
45490     /**
45491      * Hides this region.
45492      */
45493     hide : function(){
45494         if(!this.collapsed){
45495             this.el.dom.style.left = "-2000px";
45496             this.el.hide();
45497         }else{
45498             this.collapsedEl.dom.style.left = "-2000px";
45499             this.collapsedEl.hide();
45500         }
45501         this.visible = false;
45502         this.fireEvent("visibilitychange", this, false);
45503     },
45504
45505     /**
45506      * Shows this region if it was previously hidden.
45507      */
45508     show : function(){
45509         if(!this.collapsed){
45510             this.el.show();
45511         }else{
45512             this.collapsedEl.show();
45513         }
45514         this.visible = true;
45515         this.fireEvent("visibilitychange", this, true);
45516     },
45517
45518     closeClicked : function(){
45519         if(this.activePanel){
45520             this.remove(this.activePanel);
45521         }
45522     },
45523
45524     collapseClick : function(e){
45525         if(this.isSlid){
45526            e.stopPropagation();
45527            this.slideIn();
45528         }else{
45529            e.stopPropagation();
45530            this.slideOut();
45531         }
45532     },
45533
45534     /**
45535      * Collapses this region.
45536      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
45537      */
45538     collapse : function(skipAnim){
45539         if(this.collapsed) return;
45540         this.collapsed = true;
45541         if(this.split){
45542             this.split.el.hide();
45543         }
45544         if(this.config.animate && skipAnim !== true){
45545             this.fireEvent("invalidated", this);
45546             this.animateCollapse();
45547         }else{
45548             this.el.setLocation(-20000,-20000);
45549             this.el.hide();
45550             this.collapsedEl.show();
45551             this.fireEvent("collapsed", this);
45552             this.fireEvent("invalidated", this);
45553         }
45554     },
45555
45556     animateCollapse : function(){
45557         // overridden
45558     },
45559
45560     /**
45561      * Expands this region if it was previously collapsed.
45562      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
45563      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
45564      */
45565     expand : function(e, skipAnim){
45566         if(e) e.stopPropagation();
45567         if(!this.collapsed || this.el.hasActiveFx()) return;
45568         if(this.isSlid){
45569             this.afterSlideIn();
45570             skipAnim = true;
45571         }
45572         this.collapsed = false;
45573         if(this.config.animate && skipAnim !== true){
45574             this.animateExpand();
45575         }else{
45576             this.el.show();
45577             if(this.split){
45578                 this.split.el.show();
45579             }
45580             this.collapsedEl.setLocation(-2000,-2000);
45581             this.collapsedEl.hide();
45582             this.fireEvent("invalidated", this);
45583             this.fireEvent("expanded", this);
45584         }
45585     },
45586
45587     animateExpand : function(){
45588         // overridden
45589     },
45590
45591     initTabs : function()
45592     {
45593         this.bodyEl.setStyle("overflow", "hidden");
45594         var ts = new Roo.TabPanel(
45595                 this.bodyEl.dom,
45596                 {
45597                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
45598                     disableTooltips: this.config.disableTabTips,
45599                     toolbar : this.config.toolbar
45600                 }
45601         );
45602         if(this.config.hideTabs){
45603             ts.stripWrap.setDisplayed(false);
45604         }
45605         this.tabs = ts;
45606         ts.resizeTabs = this.config.resizeTabs === true;
45607         ts.minTabWidth = this.config.minTabWidth || 40;
45608         ts.maxTabWidth = this.config.maxTabWidth || 250;
45609         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
45610         ts.monitorResize = false;
45611         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
45612         ts.bodyEl.addClass('x-layout-tabs-body');
45613         this.panels.each(this.initPanelAsTab, this);
45614     },
45615
45616     initPanelAsTab : function(panel){
45617         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
45618                     this.config.closeOnTab && panel.isClosable());
45619         if(panel.tabTip !== undefined){
45620             ti.setTooltip(panel.tabTip);
45621         }
45622         ti.on("activate", function(){
45623               this.setActivePanel(panel);
45624         }, this);
45625         if(this.config.closeOnTab){
45626             ti.on("beforeclose", function(t, e){
45627                 e.cancel = true;
45628                 this.remove(panel);
45629             }, this);
45630         }
45631         return ti;
45632     },
45633
45634     updatePanelTitle : function(panel, title){
45635         if(this.activePanel == panel){
45636             this.updateTitle(title);
45637         }
45638         if(this.tabs){
45639             var ti = this.tabs.getTab(panel.getEl().id);
45640             ti.setText(title);
45641             if(panel.tabTip !== undefined){
45642                 ti.setTooltip(panel.tabTip);
45643             }
45644         }
45645     },
45646
45647     updateTitle : function(title){
45648         if(this.titleTextEl && !this.config.title){
45649             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
45650         }
45651     },
45652
45653     setActivePanel : function(panel){
45654         panel = this.getPanel(panel);
45655         if(this.activePanel && this.activePanel != panel){
45656             this.activePanel.setActiveState(false);
45657         }
45658         this.activePanel = panel;
45659         panel.setActiveState(true);
45660         if(this.panelSize){
45661             panel.setSize(this.panelSize.width, this.panelSize.height);
45662         }
45663         if(this.closeBtn){
45664             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
45665         }
45666         this.updateTitle(panel.getTitle());
45667         if(this.tabs){
45668             this.fireEvent("invalidated", this);
45669         }
45670         this.fireEvent("panelactivated", this, panel);
45671     },
45672
45673     /**
45674      * Shows the specified panel.
45675      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
45676      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
45677      */
45678     showPanel : function(panel){
45679         if(panel = this.getPanel(panel)){
45680             if(this.tabs){
45681                 var tab = this.tabs.getTab(panel.getEl().id);
45682                 if(tab.isHidden()){
45683                     this.tabs.unhideTab(tab.id);
45684                 }
45685                 tab.activate();
45686             }else{
45687                 this.setActivePanel(panel);
45688             }
45689         }
45690         return panel;
45691     },
45692
45693     /**
45694      * Get the active panel for this region.
45695      * @return {Roo.ContentPanel} The active panel or null
45696      */
45697     getActivePanel : function(){
45698         return this.activePanel;
45699     },
45700
45701     validateVisibility : function(){
45702         if(this.panels.getCount() < 1){
45703             this.updateTitle("&#160;");
45704             this.closeBtn.hide();
45705             this.hide();
45706         }else{
45707             if(!this.isVisible()){
45708                 this.show();
45709             }
45710         }
45711     },
45712
45713     /**
45714      * Adds the passed ContentPanel(s) to this region.
45715      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
45716      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
45717      */
45718     add : function(panel){
45719         if(arguments.length > 1){
45720             for(var i = 0, len = arguments.length; i < len; i++) {
45721                 this.add(arguments[i]);
45722             }
45723             return null;
45724         }
45725         if(this.hasPanel(panel)){
45726             this.showPanel(panel);
45727             return panel;
45728         }
45729         panel.setRegion(this);
45730         this.panels.add(panel);
45731         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
45732             this.bodyEl.dom.appendChild(panel.getEl().dom);
45733             if(panel.background !== true){
45734                 this.setActivePanel(panel);
45735             }
45736             this.fireEvent("paneladded", this, panel);
45737             return panel;
45738         }
45739         if(!this.tabs){
45740             this.initTabs();
45741         }else{
45742             this.initPanelAsTab(panel);
45743         }
45744         if(panel.background !== true){
45745             this.tabs.activate(panel.getEl().id);
45746         }
45747         this.fireEvent("paneladded", this, panel);
45748         return panel;
45749     },
45750
45751     /**
45752      * Hides the tab for the specified panel.
45753      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
45754      */
45755     hidePanel : function(panel){
45756         if(this.tabs && (panel = this.getPanel(panel))){
45757             this.tabs.hideTab(panel.getEl().id);
45758         }
45759     },
45760
45761     /**
45762      * Unhides the tab for a previously hidden panel.
45763      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
45764      */
45765     unhidePanel : function(panel){
45766         if(this.tabs && (panel = this.getPanel(panel))){
45767             this.tabs.unhideTab(panel.getEl().id);
45768         }
45769     },
45770
45771     clearPanels : function(){
45772         while(this.panels.getCount() > 0){
45773              this.remove(this.panels.first());
45774         }
45775     },
45776
45777     /**
45778      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
45779      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
45780      * @param {Boolean} preservePanel Overrides the config preservePanel option
45781      * @return {Roo.ContentPanel} The panel that was removed
45782      */
45783     remove : function(panel, preservePanel){
45784         panel = this.getPanel(panel);
45785         if(!panel){
45786             return null;
45787         }
45788         var e = {};
45789         this.fireEvent("beforeremove", this, panel, e);
45790         if(e.cancel === true){
45791             return null;
45792         }
45793         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
45794         var panelId = panel.getId();
45795         this.panels.removeKey(panelId);
45796         if(preservePanel){
45797             document.body.appendChild(panel.getEl().dom);
45798         }
45799         if(this.tabs){
45800             this.tabs.removeTab(panel.getEl().id);
45801         }else if (!preservePanel){
45802             this.bodyEl.dom.removeChild(panel.getEl().dom);
45803         }
45804         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
45805             var p = this.panels.first();
45806             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
45807             tempEl.appendChild(p.getEl().dom);
45808             this.bodyEl.update("");
45809             this.bodyEl.dom.appendChild(p.getEl().dom);
45810             tempEl = null;
45811             this.updateTitle(p.getTitle());
45812             this.tabs = null;
45813             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
45814             this.setActivePanel(p);
45815         }
45816         panel.setRegion(null);
45817         if(this.activePanel == panel){
45818             this.activePanel = null;
45819         }
45820         if(this.config.autoDestroy !== false && preservePanel !== true){
45821             try{panel.destroy();}catch(e){}
45822         }
45823         this.fireEvent("panelremoved", this, panel);
45824         return panel;
45825     },
45826
45827     /**
45828      * Returns the TabPanel component used by this region
45829      * @return {Roo.TabPanel}
45830      */
45831     getTabs : function(){
45832         return this.tabs;
45833     },
45834
45835     createTool : function(parentEl, className){
45836         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
45837             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
45838         btn.addClassOnOver("x-layout-tools-button-over");
45839         return btn;
45840     }
45841 });/*
45842  * Based on:
45843  * Ext JS Library 1.1.1
45844  * Copyright(c) 2006-2007, Ext JS, LLC.
45845  *
45846  * Originally Released Under LGPL - original licence link has changed is not relivant.
45847  *
45848  * Fork - LGPL
45849  * <script type="text/javascript">
45850  */
45851  
45852
45853
45854 /**
45855  * @class Roo.SplitLayoutRegion
45856  * @extends Roo.LayoutRegion
45857  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
45858  */
45859 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
45860     this.cursor = cursor;
45861     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
45862 };
45863
45864 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
45865     splitTip : "Drag to resize.",
45866     collapsibleSplitTip : "Drag to resize. Double click to hide.",
45867     useSplitTips : false,
45868
45869     applyConfig : function(config){
45870         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
45871         if(config.split){
45872             if(!this.split){
45873                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
45874                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
45875                 /** The SplitBar for this region 
45876                 * @type Roo.SplitBar */
45877                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
45878                 this.split.on("moved", this.onSplitMove, this);
45879                 this.split.useShim = config.useShim === true;
45880                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
45881                 if(this.useSplitTips){
45882                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
45883                 }
45884                 if(config.collapsible){
45885                     this.split.el.on("dblclick", this.collapse,  this);
45886                 }
45887             }
45888             if(typeof config.minSize != "undefined"){
45889                 this.split.minSize = config.minSize;
45890             }
45891             if(typeof config.maxSize != "undefined"){
45892                 this.split.maxSize = config.maxSize;
45893             }
45894             if(config.hideWhenEmpty || config.hidden || config.collapsed){
45895                 this.hideSplitter();
45896             }
45897         }
45898     },
45899
45900     getHMaxSize : function(){
45901          var cmax = this.config.maxSize || 10000;
45902          var center = this.mgr.getRegion("center");
45903          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
45904     },
45905
45906     getVMaxSize : function(){
45907          var cmax = this.config.maxSize || 10000;
45908          var center = this.mgr.getRegion("center");
45909          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
45910     },
45911
45912     onSplitMove : function(split, newSize){
45913         this.fireEvent("resized", this, newSize);
45914     },
45915     
45916     /** 
45917      * Returns the {@link Roo.SplitBar} for this region.
45918      * @return {Roo.SplitBar}
45919      */
45920     getSplitBar : function(){
45921         return this.split;
45922     },
45923     
45924     hide : function(){
45925         this.hideSplitter();
45926         Roo.SplitLayoutRegion.superclass.hide.call(this);
45927     },
45928
45929     hideSplitter : function(){
45930         if(this.split){
45931             this.split.el.setLocation(-2000,-2000);
45932             this.split.el.hide();
45933         }
45934     },
45935
45936     show : function(){
45937         if(this.split){
45938             this.split.el.show();
45939         }
45940         Roo.SplitLayoutRegion.superclass.show.call(this);
45941     },
45942     
45943     beforeSlide: function(){
45944         if(Roo.isGecko){// firefox overflow auto bug workaround
45945             this.bodyEl.clip();
45946             if(this.tabs) this.tabs.bodyEl.clip();
45947             if(this.activePanel){
45948                 this.activePanel.getEl().clip();
45949                 
45950                 if(this.activePanel.beforeSlide){
45951                     this.activePanel.beforeSlide();
45952                 }
45953             }
45954         }
45955     },
45956     
45957     afterSlide : function(){
45958         if(Roo.isGecko){// firefox overflow auto bug workaround
45959             this.bodyEl.unclip();
45960             if(this.tabs) this.tabs.bodyEl.unclip();
45961             if(this.activePanel){
45962                 this.activePanel.getEl().unclip();
45963                 if(this.activePanel.afterSlide){
45964                     this.activePanel.afterSlide();
45965                 }
45966             }
45967         }
45968     },
45969
45970     initAutoHide : function(){
45971         if(this.autoHide !== false){
45972             if(!this.autoHideHd){
45973                 var st = new Roo.util.DelayedTask(this.slideIn, this);
45974                 this.autoHideHd = {
45975                     "mouseout": function(e){
45976                         if(!e.within(this.el, true)){
45977                             st.delay(500);
45978                         }
45979                     },
45980                     "mouseover" : function(e){
45981                         st.cancel();
45982                     },
45983                     scope : this
45984                 };
45985             }
45986             this.el.on(this.autoHideHd);
45987         }
45988     },
45989
45990     clearAutoHide : function(){
45991         if(this.autoHide !== false){
45992             this.el.un("mouseout", this.autoHideHd.mouseout);
45993             this.el.un("mouseover", this.autoHideHd.mouseover);
45994         }
45995     },
45996
45997     clearMonitor : function(){
45998         Roo.get(document).un("click", this.slideInIf, this);
45999     },
46000
46001     // these names are backwards but not changed for compat
46002     slideOut : function(){
46003         if(this.isSlid || this.el.hasActiveFx()){
46004             return;
46005         }
46006         this.isSlid = true;
46007         if(this.collapseBtn){
46008             this.collapseBtn.hide();
46009         }
46010         this.closeBtnState = this.closeBtn.getStyle('display');
46011         this.closeBtn.hide();
46012         if(this.stickBtn){
46013             this.stickBtn.show();
46014         }
46015         this.el.show();
46016         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
46017         this.beforeSlide();
46018         this.el.setStyle("z-index", 10001);
46019         this.el.slideIn(this.getSlideAnchor(), {
46020             callback: function(){
46021                 this.afterSlide();
46022                 this.initAutoHide();
46023                 Roo.get(document).on("click", this.slideInIf, this);
46024                 this.fireEvent("slideshow", this);
46025             },
46026             scope: this,
46027             block: true
46028         });
46029     },
46030
46031     afterSlideIn : function(){
46032         this.clearAutoHide();
46033         this.isSlid = false;
46034         this.clearMonitor();
46035         this.el.setStyle("z-index", "");
46036         if(this.collapseBtn){
46037             this.collapseBtn.show();
46038         }
46039         this.closeBtn.setStyle('display', this.closeBtnState);
46040         if(this.stickBtn){
46041             this.stickBtn.hide();
46042         }
46043         this.fireEvent("slidehide", this);
46044     },
46045
46046     slideIn : function(cb){
46047         if(!this.isSlid || this.el.hasActiveFx()){
46048             Roo.callback(cb);
46049             return;
46050         }
46051         this.isSlid = false;
46052         this.beforeSlide();
46053         this.el.slideOut(this.getSlideAnchor(), {
46054             callback: function(){
46055                 this.el.setLeftTop(-10000, -10000);
46056                 this.afterSlide();
46057                 this.afterSlideIn();
46058                 Roo.callback(cb);
46059             },
46060             scope: this,
46061             block: true
46062         });
46063     },
46064     
46065     slideInIf : function(e){
46066         if(!e.within(this.el)){
46067             this.slideIn();
46068         }
46069     },
46070
46071     animateCollapse : function(){
46072         this.beforeSlide();
46073         this.el.setStyle("z-index", 20000);
46074         var anchor = this.getSlideAnchor();
46075         this.el.slideOut(anchor, {
46076             callback : function(){
46077                 this.el.setStyle("z-index", "");
46078                 this.collapsedEl.slideIn(anchor, {duration:.3});
46079                 this.afterSlide();
46080                 this.el.setLocation(-10000,-10000);
46081                 this.el.hide();
46082                 this.fireEvent("collapsed", this);
46083             },
46084             scope: this,
46085             block: true
46086         });
46087     },
46088
46089     animateExpand : function(){
46090         this.beforeSlide();
46091         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
46092         this.el.setStyle("z-index", 20000);
46093         this.collapsedEl.hide({
46094             duration:.1
46095         });
46096         this.el.slideIn(this.getSlideAnchor(), {
46097             callback : function(){
46098                 this.el.setStyle("z-index", "");
46099                 this.afterSlide();
46100                 if(this.split){
46101                     this.split.el.show();
46102                 }
46103                 this.fireEvent("invalidated", this);
46104                 this.fireEvent("expanded", this);
46105             },
46106             scope: this,
46107             block: true
46108         });
46109     },
46110
46111     anchors : {
46112         "west" : "left",
46113         "east" : "right",
46114         "north" : "top",
46115         "south" : "bottom"
46116     },
46117
46118     sanchors : {
46119         "west" : "l",
46120         "east" : "r",
46121         "north" : "t",
46122         "south" : "b"
46123     },
46124
46125     canchors : {
46126         "west" : "tl-tr",
46127         "east" : "tr-tl",
46128         "north" : "tl-bl",
46129         "south" : "bl-tl"
46130     },
46131
46132     getAnchor : function(){
46133         return this.anchors[this.position];
46134     },
46135
46136     getCollapseAnchor : function(){
46137         return this.canchors[this.position];
46138     },
46139
46140     getSlideAnchor : function(){
46141         return this.sanchors[this.position];
46142     },
46143
46144     getAlignAdj : function(){
46145         var cm = this.cmargins;
46146         switch(this.position){
46147             case "west":
46148                 return [0, 0];
46149             break;
46150             case "east":
46151                 return [0, 0];
46152             break;
46153             case "north":
46154                 return [0, 0];
46155             break;
46156             case "south":
46157                 return [0, 0];
46158             break;
46159         }
46160     },
46161
46162     getExpandAdj : function(){
46163         var c = this.collapsedEl, cm = this.cmargins;
46164         switch(this.position){
46165             case "west":
46166                 return [-(cm.right+c.getWidth()+cm.left), 0];
46167             break;
46168             case "east":
46169                 return [cm.right+c.getWidth()+cm.left, 0];
46170             break;
46171             case "north":
46172                 return [0, -(cm.top+cm.bottom+c.getHeight())];
46173             break;
46174             case "south":
46175                 return [0, cm.top+cm.bottom+c.getHeight()];
46176             break;
46177         }
46178     }
46179 });/*
46180  * Based on:
46181  * Ext JS Library 1.1.1
46182  * Copyright(c) 2006-2007, Ext JS, LLC.
46183  *
46184  * Originally Released Under LGPL - original licence link has changed is not relivant.
46185  *
46186  * Fork - LGPL
46187  * <script type="text/javascript">
46188  */
46189 /*
46190  * These classes are private internal classes
46191  */
46192 Roo.CenterLayoutRegion = function(mgr, config){
46193     Roo.LayoutRegion.call(this, mgr, config, "center");
46194     this.visible = true;
46195     this.minWidth = config.minWidth || 20;
46196     this.minHeight = config.minHeight || 20;
46197 };
46198
46199 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
46200     hide : function(){
46201         // center panel can't be hidden
46202     },
46203     
46204     show : function(){
46205         // center panel can't be hidden
46206     },
46207     
46208     getMinWidth: function(){
46209         return this.minWidth;
46210     },
46211     
46212     getMinHeight: function(){
46213         return this.minHeight;
46214     }
46215 });
46216
46217
46218 Roo.NorthLayoutRegion = function(mgr, config){
46219     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
46220     if(this.split){
46221         this.split.placement = Roo.SplitBar.TOP;
46222         this.split.orientation = Roo.SplitBar.VERTICAL;
46223         this.split.el.addClass("x-layout-split-v");
46224     }
46225     var size = config.initialSize || config.height;
46226     if(typeof size != "undefined"){
46227         this.el.setHeight(size);
46228     }
46229 };
46230 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
46231     orientation: Roo.SplitBar.VERTICAL,
46232     getBox : function(){
46233         if(this.collapsed){
46234             return this.collapsedEl.getBox();
46235         }
46236         var box = this.el.getBox();
46237         if(this.split){
46238             box.height += this.split.el.getHeight();
46239         }
46240         return box;
46241     },
46242     
46243     updateBox : function(box){
46244         if(this.split && !this.collapsed){
46245             box.height -= this.split.el.getHeight();
46246             this.split.el.setLeft(box.x);
46247             this.split.el.setTop(box.y+box.height);
46248             this.split.el.setWidth(box.width);
46249         }
46250         if(this.collapsed){
46251             this.updateBody(box.width, null);
46252         }
46253         Roo.LayoutRegion.prototype.updateBox.call(this, box);
46254     }
46255 });
46256
46257 Roo.SouthLayoutRegion = function(mgr, config){
46258     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
46259     if(this.split){
46260         this.split.placement = Roo.SplitBar.BOTTOM;
46261         this.split.orientation = Roo.SplitBar.VERTICAL;
46262         this.split.el.addClass("x-layout-split-v");
46263     }
46264     var size = config.initialSize || config.height;
46265     if(typeof size != "undefined"){
46266         this.el.setHeight(size);
46267     }
46268 };
46269 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
46270     orientation: Roo.SplitBar.VERTICAL,
46271     getBox : function(){
46272         if(this.collapsed){
46273             return this.collapsedEl.getBox();
46274         }
46275         var box = this.el.getBox();
46276         if(this.split){
46277             var sh = this.split.el.getHeight();
46278             box.height += sh;
46279             box.y -= sh;
46280         }
46281         return box;
46282     },
46283     
46284     updateBox : function(box){
46285         if(this.split && !this.collapsed){
46286             var sh = this.split.el.getHeight();
46287             box.height -= sh;
46288             box.y += sh;
46289             this.split.el.setLeft(box.x);
46290             this.split.el.setTop(box.y-sh);
46291             this.split.el.setWidth(box.width);
46292         }
46293         if(this.collapsed){
46294             this.updateBody(box.width, null);
46295         }
46296         Roo.LayoutRegion.prototype.updateBox.call(this, box);
46297     }
46298 });
46299
46300 Roo.EastLayoutRegion = function(mgr, config){
46301     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
46302     if(this.split){
46303         this.split.placement = Roo.SplitBar.RIGHT;
46304         this.split.orientation = Roo.SplitBar.HORIZONTAL;
46305         this.split.el.addClass("x-layout-split-h");
46306     }
46307     var size = config.initialSize || config.width;
46308     if(typeof size != "undefined"){
46309         this.el.setWidth(size);
46310     }
46311 };
46312 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
46313     orientation: Roo.SplitBar.HORIZONTAL,
46314     getBox : function(){
46315         if(this.collapsed){
46316             return this.collapsedEl.getBox();
46317         }
46318         var box = this.el.getBox();
46319         if(this.split){
46320             var sw = this.split.el.getWidth();
46321             box.width += sw;
46322             box.x -= sw;
46323         }
46324         return box;
46325     },
46326
46327     updateBox : function(box){
46328         if(this.split && !this.collapsed){
46329             var sw = this.split.el.getWidth();
46330             box.width -= sw;
46331             this.split.el.setLeft(box.x);
46332             this.split.el.setTop(box.y);
46333             this.split.el.setHeight(box.height);
46334             box.x += sw;
46335         }
46336         if(this.collapsed){
46337             this.updateBody(null, box.height);
46338         }
46339         Roo.LayoutRegion.prototype.updateBox.call(this, box);
46340     }
46341 });
46342
46343 Roo.WestLayoutRegion = function(mgr, config){
46344     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
46345     if(this.split){
46346         this.split.placement = Roo.SplitBar.LEFT;
46347         this.split.orientation = Roo.SplitBar.HORIZONTAL;
46348         this.split.el.addClass("x-layout-split-h");
46349     }
46350     var size = config.initialSize || config.width;
46351     if(typeof size != "undefined"){
46352         this.el.setWidth(size);
46353     }
46354 };
46355 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
46356     orientation: Roo.SplitBar.HORIZONTAL,
46357     getBox : function(){
46358         if(this.collapsed){
46359             return this.collapsedEl.getBox();
46360         }
46361         var box = this.el.getBox();
46362         if(this.split){
46363             box.width += this.split.el.getWidth();
46364         }
46365         return box;
46366     },
46367     
46368     updateBox : function(box){
46369         if(this.split && !this.collapsed){
46370             var sw = this.split.el.getWidth();
46371             box.width -= sw;
46372             this.split.el.setLeft(box.x+box.width);
46373             this.split.el.setTop(box.y);
46374             this.split.el.setHeight(box.height);
46375         }
46376         if(this.collapsed){
46377             this.updateBody(null, box.height);
46378         }
46379         Roo.LayoutRegion.prototype.updateBox.call(this, box);
46380     }
46381 });
46382 /*
46383  * Based on:
46384  * Ext JS Library 1.1.1
46385  * Copyright(c) 2006-2007, Ext JS, LLC.
46386  *
46387  * Originally Released Under LGPL - original licence link has changed is not relivant.
46388  *
46389  * Fork - LGPL
46390  * <script type="text/javascript">
46391  */
46392  
46393  
46394 /*
46395  * Private internal class for reading and applying state
46396  */
46397 Roo.LayoutStateManager = function(layout){
46398      // default empty state
46399      this.state = {
46400         north: {},
46401         south: {},
46402         east: {},
46403         west: {}       
46404     };
46405 };
46406
46407 Roo.LayoutStateManager.prototype = {
46408     init : function(layout, provider){
46409         this.provider = provider;
46410         var state = provider.get(layout.id+"-layout-state");
46411         if(state){
46412             var wasUpdating = layout.isUpdating();
46413             if(!wasUpdating){
46414                 layout.beginUpdate();
46415             }
46416             for(var key in state){
46417                 if(typeof state[key] != "function"){
46418                     var rstate = state[key];
46419                     var r = layout.getRegion(key);
46420                     if(r && rstate){
46421                         if(rstate.size){
46422                             r.resizeTo(rstate.size);
46423                         }
46424                         if(rstate.collapsed == true){
46425                             r.collapse(true);
46426                         }else{
46427                             r.expand(null, true);
46428                         }
46429                     }
46430                 }
46431             }
46432             if(!wasUpdating){
46433                 layout.endUpdate();
46434             }
46435             this.state = state; 
46436         }
46437         this.layout = layout;
46438         layout.on("regionresized", this.onRegionResized, this);
46439         layout.on("regioncollapsed", this.onRegionCollapsed, this);
46440         layout.on("regionexpanded", this.onRegionExpanded, this);
46441     },
46442     
46443     storeState : function(){
46444         this.provider.set(this.layout.id+"-layout-state", this.state);
46445     },
46446     
46447     onRegionResized : function(region, newSize){
46448         this.state[region.getPosition()].size = newSize;
46449         this.storeState();
46450     },
46451     
46452     onRegionCollapsed : function(region){
46453         this.state[region.getPosition()].collapsed = true;
46454         this.storeState();
46455     },
46456     
46457     onRegionExpanded : function(region){
46458         this.state[region.getPosition()].collapsed = false;
46459         this.storeState();
46460     }
46461 };/*
46462  * Based on:
46463  * Ext JS Library 1.1.1
46464  * Copyright(c) 2006-2007, Ext JS, LLC.
46465  *
46466  * Originally Released Under LGPL - original licence link has changed is not relivant.
46467  *
46468  * Fork - LGPL
46469  * <script type="text/javascript">
46470  */
46471 /**
46472  * @class Roo.ContentPanel
46473  * @extends Roo.util.Observable
46474  * A basic ContentPanel element.
46475  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
46476  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
46477  * @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
46478  * @cfg {Boolean}   closable      True if the panel can be closed/removed
46479  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
46480  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
46481  * @cfg {Toolbar}   toolbar       A toolbar for this panel
46482  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
46483  * @cfg {String} title          The title for this panel
46484  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
46485  * @cfg {String} url            Calls {@link #setUrl} with this value
46486  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
46487  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
46488  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
46489  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
46490
46491  * @constructor
46492  * Create a new ContentPanel.
46493  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
46494  * @param {String/Object} config A string to set only the title or a config object
46495  * @param {String} content (optional) Set the HTML content for this panel
46496  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
46497  */
46498 Roo.ContentPanel = function(el, config, content){
46499     
46500      
46501     /*
46502     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
46503         config = el;
46504         el = Roo.id();
46505     }
46506     if (config && config.parentLayout) { 
46507         el = config.parentLayout.el.createChild(); 
46508     }
46509     */
46510     if(el.autoCreate){ // xtype is available if this is called from factory
46511         config = el;
46512         el = Roo.id();
46513     }
46514     this.el = Roo.get(el);
46515     if(!this.el && config && config.autoCreate){
46516         if(typeof config.autoCreate == "object"){
46517             if(!config.autoCreate.id){
46518                 config.autoCreate.id = config.id||el;
46519             }
46520             this.el = Roo.DomHelper.append(document.body,
46521                         config.autoCreate, true);
46522         }else{
46523             this.el = Roo.DomHelper.append(document.body,
46524                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
46525         }
46526     }
46527     this.closable = false;
46528     this.loaded = false;
46529     this.active = false;
46530     if(typeof config == "string"){
46531         this.title = config;
46532     }else{
46533         Roo.apply(this, config);
46534     }
46535     
46536     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
46537         this.wrapEl = this.el.wrap();
46538         this.toolbar.container = this.el.insertSibling(false, 'before');
46539         this.toolbar = new Roo.Toolbar(this.toolbar);
46540     }
46541     
46542     
46543     
46544     if(this.resizeEl){
46545         this.resizeEl = Roo.get(this.resizeEl, true);
46546     }else{
46547         this.resizeEl = this.el;
46548     }
46549     this.addEvents({
46550         /**
46551          * @event activate
46552          * Fires when this panel is activated. 
46553          * @param {Roo.ContentPanel} this
46554          */
46555         "activate" : true,
46556         /**
46557          * @event deactivate
46558          * Fires when this panel is activated. 
46559          * @param {Roo.ContentPanel} this
46560          */
46561         "deactivate" : true,
46562
46563         /**
46564          * @event resize
46565          * Fires when this panel is resized if fitToFrame is true.
46566          * @param {Roo.ContentPanel} this
46567          * @param {Number} width The width after any component adjustments
46568          * @param {Number} height The height after any component adjustments
46569          */
46570         "resize" : true,
46571         
46572          /**
46573          * @event render
46574          * Fires when this tab is created
46575          * @param {Roo.ContentPanel} this
46576          */
46577         "render" : true
46578         
46579         
46580         
46581     });
46582     if(this.autoScroll){
46583         this.resizeEl.setStyle("overflow", "auto");
46584     } else {
46585         // fix randome scrolling
46586         this.el.on('scroll', function() {
46587             Roo.log('fix random scolling');
46588             this.scrollTo('top',0); 
46589         });
46590     }
46591     content = content || this.content;
46592     if(content){
46593         this.setContent(content);
46594     }
46595     if(config && config.url){
46596         this.setUrl(this.url, this.params, this.loadOnce);
46597     }
46598     
46599     
46600     
46601     Roo.ContentPanel.superclass.constructor.call(this);
46602     
46603     this.fireEvent('render', this);
46604 };
46605
46606 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
46607     tabTip:'',
46608     setRegion : function(region){
46609         this.region = region;
46610         if(region){
46611            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
46612         }else{
46613            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
46614         } 
46615     },
46616     
46617     /**
46618      * Returns the toolbar for this Panel if one was configured. 
46619      * @return {Roo.Toolbar} 
46620      */
46621     getToolbar : function(){
46622         return this.toolbar;
46623     },
46624     
46625     setActiveState : function(active){
46626         this.active = active;
46627         if(!active){
46628             this.fireEvent("deactivate", this);
46629         }else{
46630             this.fireEvent("activate", this);
46631         }
46632     },
46633     /**
46634      * Updates this panel's element
46635      * @param {String} content The new content
46636      * @param {Boolean} loadScripts (optional) true to look for and process scripts
46637     */
46638     setContent : function(content, loadScripts){
46639         this.el.update(content, loadScripts);
46640     },
46641
46642     ignoreResize : function(w, h){
46643         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
46644             return true;
46645         }else{
46646             this.lastSize = {width: w, height: h};
46647             return false;
46648         }
46649     },
46650     /**
46651      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
46652      * @return {Roo.UpdateManager} The UpdateManager
46653      */
46654     getUpdateManager : function(){
46655         return this.el.getUpdateManager();
46656     },
46657      /**
46658      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
46659      * @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:
46660 <pre><code>
46661 panel.load({
46662     url: "your-url.php",
46663     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
46664     callback: yourFunction,
46665     scope: yourObject, //(optional scope)
46666     discardUrl: false,
46667     nocache: false,
46668     text: "Loading...",
46669     timeout: 30,
46670     scripts: false
46671 });
46672 </code></pre>
46673      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
46674      * 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.
46675      * @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}
46676      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
46677      * @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.
46678      * @return {Roo.ContentPanel} this
46679      */
46680     load : function(){
46681         var um = this.el.getUpdateManager();
46682         um.update.apply(um, arguments);
46683         return this;
46684     },
46685
46686
46687     /**
46688      * 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.
46689      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
46690      * @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)
46691      * @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)
46692      * @return {Roo.UpdateManager} The UpdateManager
46693      */
46694     setUrl : function(url, params, loadOnce){
46695         if(this.refreshDelegate){
46696             this.removeListener("activate", this.refreshDelegate);
46697         }
46698         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
46699         this.on("activate", this.refreshDelegate);
46700         return this.el.getUpdateManager();
46701     },
46702     
46703     _handleRefresh : function(url, params, loadOnce){
46704         if(!loadOnce || !this.loaded){
46705             var updater = this.el.getUpdateManager();
46706             updater.update(url, params, this._setLoaded.createDelegate(this));
46707         }
46708     },
46709     
46710     _setLoaded : function(){
46711         this.loaded = true;
46712     }, 
46713     
46714     /**
46715      * Returns this panel's id
46716      * @return {String} 
46717      */
46718     getId : function(){
46719         return this.el.id;
46720     },
46721     
46722     /** 
46723      * Returns this panel's element - used by regiosn to add.
46724      * @return {Roo.Element} 
46725      */
46726     getEl : function(){
46727         return this.wrapEl || this.el;
46728     },
46729     
46730     adjustForComponents : function(width, height){
46731         if(this.resizeEl != this.el){
46732             width -= this.el.getFrameWidth('lr');
46733             height -= this.el.getFrameWidth('tb');
46734         }
46735         if(this.toolbar){
46736             var te = this.toolbar.getEl();
46737             height -= te.getHeight();
46738             te.setWidth(width);
46739         }
46740         if(this.adjustments){
46741             width += this.adjustments[0];
46742             height += this.adjustments[1];
46743         }
46744         return {"width": width, "height": height};
46745     },
46746     
46747     setSize : function(width, height){
46748         if(this.fitToFrame && !this.ignoreResize(width, height)){
46749             if(this.fitContainer && this.resizeEl != this.el){
46750                 this.el.setSize(width, height);
46751             }
46752             var size = this.adjustForComponents(width, height);
46753             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
46754             this.fireEvent('resize', this, size.width, size.height);
46755         }
46756     },
46757     
46758     /**
46759      * Returns this panel's title
46760      * @return {String} 
46761      */
46762     getTitle : function(){
46763         return this.title;
46764     },
46765     
46766     /**
46767      * Set this panel's title
46768      * @param {String} title
46769      */
46770     setTitle : function(title){
46771         this.title = title;
46772         if(this.region){
46773             this.region.updatePanelTitle(this, title);
46774         }
46775     },
46776     
46777     /**
46778      * Returns true is this panel was configured to be closable
46779      * @return {Boolean} 
46780      */
46781     isClosable : function(){
46782         return this.closable;
46783     },
46784     
46785     beforeSlide : function(){
46786         this.el.clip();
46787         this.resizeEl.clip();
46788     },
46789     
46790     afterSlide : function(){
46791         this.el.unclip();
46792         this.resizeEl.unclip();
46793     },
46794     
46795     /**
46796      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
46797      *   Will fail silently if the {@link #setUrl} method has not been called.
46798      *   This does not activate the panel, just updates its content.
46799      */
46800     refresh : function(){
46801         if(this.refreshDelegate){
46802            this.loaded = false;
46803            this.refreshDelegate();
46804         }
46805     },
46806     
46807     /**
46808      * Destroys this panel
46809      */
46810     destroy : function(){
46811         this.el.removeAllListeners();
46812         var tempEl = document.createElement("span");
46813         tempEl.appendChild(this.el.dom);
46814         tempEl.innerHTML = "";
46815         this.el.remove();
46816         this.el = null;
46817     },
46818     
46819     /**
46820      * form - if the content panel contains a form - this is a reference to it.
46821      * @type {Roo.form.Form}
46822      */
46823     form : false,
46824     /**
46825      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
46826      *    This contains a reference to it.
46827      * @type {Roo.View}
46828      */
46829     view : false,
46830     
46831       /**
46832      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
46833      * <pre><code>
46834
46835 layout.addxtype({
46836        xtype : 'Form',
46837        items: [ .... ]
46838    }
46839 );
46840
46841 </code></pre>
46842      * @param {Object} cfg Xtype definition of item to add.
46843      */
46844     
46845     addxtype : function(cfg) {
46846         // add form..
46847         if (cfg.xtype.match(/^Form$/)) {
46848             var el = this.el.createChild();
46849
46850             this.form = new  Roo.form.Form(cfg);
46851             
46852             
46853             if ( this.form.allItems.length) this.form.render(el.dom);
46854             return this.form;
46855         }
46856         // should only have one of theses..
46857         if (['View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
46858             // views..
46859             cfg.el = this.el.appendChild(document.createElement("div"));
46860             // factory?
46861             
46862             var ret = new Roo.factory(cfg);
46863             ret.render && ret.render(false, ''); // render blank..
46864             this.view = ret;
46865             return ret;
46866         }
46867         return false;
46868     }
46869 });
46870
46871 /**
46872  * @class Roo.GridPanel
46873  * @extends Roo.ContentPanel
46874  * @constructor
46875  * Create a new GridPanel.
46876  * @param {Roo.grid.Grid} grid The grid for this panel
46877  * @param {String/Object} config A string to set only the panel's title, or a config object
46878  */
46879 Roo.GridPanel = function(grid, config){
46880     
46881   
46882     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
46883         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
46884         
46885     this.wrapper.dom.appendChild(grid.getGridEl().dom);
46886     
46887     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
46888     
46889     if(this.toolbar){
46890         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
46891     }
46892     // xtype created footer. - not sure if will work as we normally have to render first..
46893     if (this.footer && !this.footer.el && this.footer.xtype) {
46894         
46895         this.footer.container = this.grid.getView().getFooterPanel(true);
46896         this.footer.dataSource = this.grid.dataSource;
46897         this.footer = Roo.factory(this.footer, Roo);
46898         
46899     }
46900     
46901     grid.monitorWindowResize = false; // turn off autosizing
46902     grid.autoHeight = false;
46903     grid.autoWidth = false;
46904     this.grid = grid;
46905     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
46906 };
46907
46908 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
46909     getId : function(){
46910         return this.grid.id;
46911     },
46912     
46913     /**
46914      * Returns the grid for this panel
46915      * @return {Roo.grid.Grid} 
46916      */
46917     getGrid : function(){
46918         return this.grid;    
46919     },
46920     
46921     setSize : function(width, height){
46922         if(!this.ignoreResize(width, height)){
46923             var grid = this.grid;
46924             var size = this.adjustForComponents(width, height);
46925             grid.getGridEl().setSize(size.width, size.height);
46926             grid.autoSize();
46927         }
46928     },
46929     
46930     beforeSlide : function(){
46931         this.grid.getView().scroller.clip();
46932     },
46933     
46934     afterSlide : function(){
46935         this.grid.getView().scroller.unclip();
46936     },
46937     
46938     destroy : function(){
46939         this.grid.destroy();
46940         delete this.grid;
46941         Roo.GridPanel.superclass.destroy.call(this); 
46942     }
46943 });
46944
46945
46946 /**
46947  * @class Roo.NestedLayoutPanel
46948  * @extends Roo.ContentPanel
46949  * @constructor
46950  * Create a new NestedLayoutPanel.
46951  * 
46952  * 
46953  * @param {Roo.BorderLayout} layout The layout for this panel
46954  * @param {String/Object} config A string to set only the title or a config object
46955  */
46956 Roo.NestedLayoutPanel = function(layout, config)
46957 {
46958     // construct with only one argument..
46959     /* FIXME - implement nicer consturctors
46960     if (layout.layout) {
46961         config = layout;
46962         layout = config.layout;
46963         delete config.layout;
46964     }
46965     if (layout.xtype && !layout.getEl) {
46966         // then layout needs constructing..
46967         layout = Roo.factory(layout, Roo);
46968     }
46969     */
46970     
46971     
46972     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
46973     
46974     layout.monitorWindowResize = false; // turn off autosizing
46975     this.layout = layout;
46976     this.layout.getEl().addClass("x-layout-nested-layout");
46977     
46978     
46979     
46980     
46981 };
46982
46983 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
46984
46985     setSize : function(width, height){
46986         if(!this.ignoreResize(width, height)){
46987             var size = this.adjustForComponents(width, height);
46988             var el = this.layout.getEl();
46989             el.setSize(size.width, size.height);
46990             var touch = el.dom.offsetWidth;
46991             this.layout.layout();
46992             // ie requires a double layout on the first pass
46993             if(Roo.isIE && !this.initialized){
46994                 this.initialized = true;
46995                 this.layout.layout();
46996             }
46997         }
46998     },
46999     
47000     // activate all subpanels if not currently active..
47001     
47002     setActiveState : function(active){
47003         this.active = active;
47004         if(!active){
47005             this.fireEvent("deactivate", this);
47006             return;
47007         }
47008         
47009         this.fireEvent("activate", this);
47010         // not sure if this should happen before or after..
47011         if (!this.layout) {
47012             return; // should not happen..
47013         }
47014         var reg = false;
47015         for (var r in this.layout.regions) {
47016             reg = this.layout.getRegion(r);
47017             if (reg.getActivePanel()) {
47018                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
47019                 reg.setActivePanel(reg.getActivePanel());
47020                 continue;
47021             }
47022             if (!reg.panels.length) {
47023                 continue;
47024             }
47025             reg.showPanel(reg.getPanel(0));
47026         }
47027         
47028         
47029         
47030         
47031     },
47032     
47033     /**
47034      * Returns the nested BorderLayout for this panel
47035      * @return {Roo.BorderLayout} 
47036      */
47037     getLayout : function(){
47038         return this.layout;
47039     },
47040     
47041      /**
47042      * Adds a xtype elements to the layout of the nested panel
47043      * <pre><code>
47044
47045 panel.addxtype({
47046        xtype : 'ContentPanel',
47047        region: 'west',
47048        items: [ .... ]
47049    }
47050 );
47051
47052 panel.addxtype({
47053         xtype : 'NestedLayoutPanel',
47054         region: 'west',
47055         layout: {
47056            center: { },
47057            west: { }   
47058         },
47059         items : [ ... list of content panels or nested layout panels.. ]
47060    }
47061 );
47062 </code></pre>
47063      * @param {Object} cfg Xtype definition of item to add.
47064      */
47065     addxtype : function(cfg) {
47066         return this.layout.addxtype(cfg);
47067     
47068     }
47069 });
47070
47071 Roo.ScrollPanel = function(el, config, content){
47072     config = config || {};
47073     config.fitToFrame = true;
47074     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
47075     
47076     this.el.dom.style.overflow = "hidden";
47077     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
47078     this.el.removeClass("x-layout-inactive-content");
47079     this.el.on("mousewheel", this.onWheel, this);
47080
47081     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
47082     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
47083     up.unselectable(); down.unselectable();
47084     up.on("click", this.scrollUp, this);
47085     down.on("click", this.scrollDown, this);
47086     up.addClassOnOver("x-scroller-btn-over");
47087     down.addClassOnOver("x-scroller-btn-over");
47088     up.addClassOnClick("x-scroller-btn-click");
47089     down.addClassOnClick("x-scroller-btn-click");
47090     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
47091
47092     this.resizeEl = this.el;
47093     this.el = wrap; this.up = up; this.down = down;
47094 };
47095
47096 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
47097     increment : 100,
47098     wheelIncrement : 5,
47099     scrollUp : function(){
47100         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
47101     },
47102
47103     scrollDown : function(){
47104         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
47105     },
47106
47107     afterScroll : function(){
47108         var el = this.resizeEl;
47109         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
47110         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
47111         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
47112     },
47113
47114     setSize : function(){
47115         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
47116         this.afterScroll();
47117     },
47118
47119     onWheel : function(e){
47120         var d = e.getWheelDelta();
47121         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
47122         this.afterScroll();
47123         e.stopEvent();
47124     },
47125
47126     setContent : function(content, loadScripts){
47127         this.resizeEl.update(content, loadScripts);
47128     }
47129
47130 });
47131
47132
47133
47134
47135
47136
47137
47138
47139
47140 /**
47141  * @class Roo.TreePanel
47142  * @extends Roo.ContentPanel
47143  * @constructor
47144  * Create a new TreePanel. - defaults to fit/scoll contents.
47145  * @param {String/Object} config A string to set only the panel's title, or a config object
47146  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
47147  */
47148 Roo.TreePanel = function(config){
47149     var el = config.el;
47150     var tree = config.tree;
47151     delete config.tree; 
47152     delete config.el; // hopefull!
47153     
47154     // wrapper for IE7 strict & safari scroll issue
47155     
47156     var treeEl = el.createChild();
47157     config.resizeEl = treeEl;
47158     
47159     
47160     
47161     Roo.TreePanel.superclass.constructor.call(this, el, config);
47162  
47163  
47164     this.tree = new Roo.tree.TreePanel(treeEl , tree);
47165     //console.log(tree);
47166     this.on('activate', function()
47167     {
47168         if (this.tree.rendered) {
47169             return;
47170         }
47171         //console.log('render tree');
47172         this.tree.render();
47173     });
47174     
47175     this.on('resize',  function (cp, w, h) {
47176             this.tree.innerCt.setWidth(w);
47177             this.tree.innerCt.setHeight(h);
47178             this.tree.innerCt.setStyle('overflow-y', 'auto');
47179     });
47180
47181         
47182     
47183 };
47184
47185 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
47186     fitToFrame : true,
47187     autoScroll : true
47188 });
47189
47190
47191
47192
47193
47194
47195
47196
47197
47198
47199
47200 /*
47201  * Based on:
47202  * Ext JS Library 1.1.1
47203  * Copyright(c) 2006-2007, Ext JS, LLC.
47204  *
47205  * Originally Released Under LGPL - original licence link has changed is not relivant.
47206  *
47207  * Fork - LGPL
47208  * <script type="text/javascript">
47209  */
47210  
47211
47212 /**
47213  * @class Roo.ReaderLayout
47214  * @extends Roo.BorderLayout
47215  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
47216  * center region containing two nested regions (a top one for a list view and one for item preview below),
47217  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
47218  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
47219  * expedites the setup of the overall layout and regions for this common application style.
47220  * Example:
47221  <pre><code>
47222 var reader = new Roo.ReaderLayout();
47223 var CP = Roo.ContentPanel;  // shortcut for adding
47224
47225 reader.beginUpdate();
47226 reader.add("north", new CP("north", "North"));
47227 reader.add("west", new CP("west", {title: "West"}));
47228 reader.add("east", new CP("east", {title: "East"}));
47229
47230 reader.regions.listView.add(new CP("listView", "List"));
47231 reader.regions.preview.add(new CP("preview", "Preview"));
47232 reader.endUpdate();
47233 </code></pre>
47234 * @constructor
47235 * Create a new ReaderLayout
47236 * @param {Object} config Configuration options
47237 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
47238 * document.body if omitted)
47239 */
47240 Roo.ReaderLayout = function(config, renderTo){
47241     var c = config || {size:{}};
47242     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
47243         north: c.north !== false ? Roo.apply({
47244             split:false,
47245             initialSize: 32,
47246             titlebar: false
47247         }, c.north) : false,
47248         west: c.west !== false ? Roo.apply({
47249             split:true,
47250             initialSize: 200,
47251             minSize: 175,
47252             maxSize: 400,
47253             titlebar: true,
47254             collapsible: true,
47255             animate: true,
47256             margins:{left:5,right:0,bottom:5,top:5},
47257             cmargins:{left:5,right:5,bottom:5,top:5}
47258         }, c.west) : false,
47259         east: c.east !== false ? Roo.apply({
47260             split:true,
47261             initialSize: 200,
47262             minSize: 175,
47263             maxSize: 400,
47264             titlebar: true,
47265             collapsible: true,
47266             animate: true,
47267             margins:{left:0,right:5,bottom:5,top:5},
47268             cmargins:{left:5,right:5,bottom:5,top:5}
47269         }, c.east) : false,
47270         center: Roo.apply({
47271             tabPosition: 'top',
47272             autoScroll:false,
47273             closeOnTab: true,
47274             titlebar:false,
47275             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
47276         }, c.center)
47277     });
47278
47279     this.el.addClass('x-reader');
47280
47281     this.beginUpdate();
47282
47283     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
47284         south: c.preview !== false ? Roo.apply({
47285             split:true,
47286             initialSize: 200,
47287             minSize: 100,
47288             autoScroll:true,
47289             collapsible:true,
47290             titlebar: true,
47291             cmargins:{top:5,left:0, right:0, bottom:0}
47292         }, c.preview) : false,
47293         center: Roo.apply({
47294             autoScroll:false,
47295             titlebar:false,
47296             minHeight:200
47297         }, c.listView)
47298     });
47299     this.add('center', new Roo.NestedLayoutPanel(inner,
47300             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
47301
47302     this.endUpdate();
47303
47304     this.regions.preview = inner.getRegion('south');
47305     this.regions.listView = inner.getRegion('center');
47306 };
47307
47308 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
47309  * Based on:
47310  * Ext JS Library 1.1.1
47311  * Copyright(c) 2006-2007, Ext JS, LLC.
47312  *
47313  * Originally Released Under LGPL - original licence link has changed is not relivant.
47314  *
47315  * Fork - LGPL
47316  * <script type="text/javascript">
47317  */
47318  
47319 /**
47320  * @class Roo.grid.Grid
47321  * @extends Roo.util.Observable
47322  * This class represents the primary interface of a component based grid control.
47323  * <br><br>Usage:<pre><code>
47324  var grid = new Roo.grid.Grid("my-container-id", {
47325      ds: myDataStore,
47326      cm: myColModel,
47327      selModel: mySelectionModel,
47328      autoSizeColumns: true,
47329      monitorWindowResize: false,
47330      trackMouseOver: true
47331  });
47332  // set any options
47333  grid.render();
47334  * </code></pre>
47335  * <b>Common Problems:</b><br/>
47336  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
47337  * element will correct this<br/>
47338  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
47339  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
47340  * are unpredictable.<br/>
47341  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
47342  * grid to calculate dimensions/offsets.<br/>
47343   * @constructor
47344  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
47345  * The container MUST have some type of size defined for the grid to fill. The container will be
47346  * automatically set to position relative if it isn't already.
47347  * @param {Object} config A config object that sets properties on this grid.
47348  */
47349 Roo.grid.Grid = function(container, config){
47350         // initialize the container
47351         this.container = Roo.get(container);
47352         this.container.update("");
47353         this.container.setStyle("overflow", "hidden");
47354     this.container.addClass('x-grid-container');
47355
47356     this.id = this.container.id;
47357
47358     Roo.apply(this, config);
47359     // check and correct shorthanded configs
47360     if(this.ds){
47361         this.dataSource = this.ds;
47362         delete this.ds;
47363     }
47364     if(this.cm){
47365         this.colModel = this.cm;
47366         delete this.cm;
47367     }
47368     if(this.sm){
47369         this.selModel = this.sm;
47370         delete this.sm;
47371     }
47372
47373     if (this.selModel) {
47374         this.selModel = Roo.factory(this.selModel, Roo.grid);
47375         this.sm = this.selModel;
47376         this.sm.xmodule = this.xmodule || false;
47377     }
47378     if (typeof(this.colModel.config) == 'undefined') {
47379         this.colModel = new Roo.grid.ColumnModel(this.colModel);
47380         this.cm = this.colModel;
47381         this.cm.xmodule = this.xmodule || false;
47382     }
47383     if (this.dataSource) {
47384         this.dataSource= Roo.factory(this.dataSource, Roo.data);
47385         this.ds = this.dataSource;
47386         this.ds.xmodule = this.xmodule || false;
47387          
47388     }
47389     
47390     
47391     
47392     if(this.width){
47393         this.container.setWidth(this.width);
47394     }
47395
47396     if(this.height){
47397         this.container.setHeight(this.height);
47398     }
47399     /** @private */
47400         this.addEvents({
47401         // raw events
47402         /**
47403          * @event click
47404          * The raw click event for the entire grid.
47405          * @param {Roo.EventObject} e
47406          */
47407         "click" : true,
47408         /**
47409          * @event dblclick
47410          * The raw dblclick event for the entire grid.
47411          * @param {Roo.EventObject} e
47412          */
47413         "dblclick" : true,
47414         /**
47415          * @event contextmenu
47416          * The raw contextmenu event for the entire grid.
47417          * @param {Roo.EventObject} e
47418          */
47419         "contextmenu" : true,
47420         /**
47421          * @event mousedown
47422          * The raw mousedown event for the entire grid.
47423          * @param {Roo.EventObject} e
47424          */
47425         "mousedown" : true,
47426         /**
47427          * @event mouseup
47428          * The raw mouseup event for the entire grid.
47429          * @param {Roo.EventObject} e
47430          */
47431         "mouseup" : true,
47432         /**
47433          * @event mouseover
47434          * The raw mouseover event for the entire grid.
47435          * @param {Roo.EventObject} e
47436          */
47437         "mouseover" : true,
47438         /**
47439          * @event mouseout
47440          * The raw mouseout event for the entire grid.
47441          * @param {Roo.EventObject} e
47442          */
47443         "mouseout" : true,
47444         /**
47445          * @event keypress
47446          * The raw keypress event for the entire grid.
47447          * @param {Roo.EventObject} e
47448          */
47449         "keypress" : true,
47450         /**
47451          * @event keydown
47452          * The raw keydown event for the entire grid.
47453          * @param {Roo.EventObject} e
47454          */
47455         "keydown" : true,
47456
47457         // custom events
47458
47459         /**
47460          * @event cellclick
47461          * Fires when a cell is clicked
47462          * @param {Grid} this
47463          * @param {Number} rowIndex
47464          * @param {Number} columnIndex
47465          * @param {Roo.EventObject} e
47466          */
47467         "cellclick" : true,
47468         /**
47469          * @event celldblclick
47470          * Fires when a cell is double clicked
47471          * @param {Grid} this
47472          * @param {Number} rowIndex
47473          * @param {Number} columnIndex
47474          * @param {Roo.EventObject} e
47475          */
47476         "celldblclick" : true,
47477         /**
47478          * @event rowclick
47479          * Fires when a row is clicked
47480          * @param {Grid} this
47481          * @param {Number} rowIndex
47482          * @param {Roo.EventObject} e
47483          */
47484         "rowclick" : true,
47485         /**
47486          * @event rowdblclick
47487          * Fires when a row is double clicked
47488          * @param {Grid} this
47489          * @param {Number} rowIndex
47490          * @param {Roo.EventObject} e
47491          */
47492         "rowdblclick" : true,
47493         /**
47494          * @event headerclick
47495          * Fires when a header is clicked
47496          * @param {Grid} this
47497          * @param {Number} columnIndex
47498          * @param {Roo.EventObject} e
47499          */
47500         "headerclick" : true,
47501         /**
47502          * @event headerdblclick
47503          * Fires when a header cell is double clicked
47504          * @param {Grid} this
47505          * @param {Number} columnIndex
47506          * @param {Roo.EventObject} e
47507          */
47508         "headerdblclick" : true,
47509         /**
47510          * @event rowcontextmenu
47511          * Fires when a row is right clicked
47512          * @param {Grid} this
47513          * @param {Number} rowIndex
47514          * @param {Roo.EventObject} e
47515          */
47516         "rowcontextmenu" : true,
47517         /**
47518          * @event cellcontextmenu
47519          * Fires when a cell is right clicked
47520          * @param {Grid} this
47521          * @param {Number} rowIndex
47522          * @param {Number} cellIndex
47523          * @param {Roo.EventObject} e
47524          */
47525          "cellcontextmenu" : true,
47526         /**
47527          * @event headercontextmenu
47528          * Fires when a header is right clicked
47529          * @param {Grid} this
47530          * @param {Number} columnIndex
47531          * @param {Roo.EventObject} e
47532          */
47533         "headercontextmenu" : true,
47534         /**
47535          * @event bodyscroll
47536          * Fires when the body element is scrolled
47537          * @param {Number} scrollLeft
47538          * @param {Number} scrollTop
47539          */
47540         "bodyscroll" : true,
47541         /**
47542          * @event columnresize
47543          * Fires when the user resizes a column
47544          * @param {Number} columnIndex
47545          * @param {Number} newSize
47546          */
47547         "columnresize" : true,
47548         /**
47549          * @event columnmove
47550          * Fires when the user moves a column
47551          * @param {Number} oldIndex
47552          * @param {Number} newIndex
47553          */
47554         "columnmove" : true,
47555         /**
47556          * @event startdrag
47557          * Fires when row(s) start being dragged
47558          * @param {Grid} this
47559          * @param {Roo.GridDD} dd The drag drop object
47560          * @param {event} e The raw browser event
47561          */
47562         "startdrag" : true,
47563         /**
47564          * @event enddrag
47565          * Fires when a drag operation is complete
47566          * @param {Grid} this
47567          * @param {Roo.GridDD} dd The drag drop object
47568          * @param {event} e The raw browser event
47569          */
47570         "enddrag" : true,
47571         /**
47572          * @event dragdrop
47573          * Fires when dragged row(s) are dropped on a valid DD target
47574          * @param {Grid} this
47575          * @param {Roo.GridDD} dd The drag drop object
47576          * @param {String} targetId The target drag drop object
47577          * @param {event} e The raw browser event
47578          */
47579         "dragdrop" : true,
47580         /**
47581          * @event dragover
47582          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
47583          * @param {Grid} this
47584          * @param {Roo.GridDD} dd The drag drop object
47585          * @param {String} targetId The target drag drop object
47586          * @param {event} e The raw browser event
47587          */
47588         "dragover" : true,
47589         /**
47590          * @event dragenter
47591          *  Fires when the dragged row(s) first cross another DD target while being dragged
47592          * @param {Grid} this
47593          * @param {Roo.GridDD} dd The drag drop object
47594          * @param {String} targetId The target drag drop object
47595          * @param {event} e The raw browser event
47596          */
47597         "dragenter" : true,
47598         /**
47599          * @event dragout
47600          * Fires when the dragged row(s) leave another DD target while being dragged
47601          * @param {Grid} this
47602          * @param {Roo.GridDD} dd The drag drop object
47603          * @param {String} targetId The target drag drop object
47604          * @param {event} e The raw browser event
47605          */
47606         "dragout" : true,
47607         /**
47608          * @event rowclass
47609          * Fires when a row is rendered, so you can change add a style to it.
47610          * @param {GridView} gridview   The grid view
47611          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
47612          */
47613         'rowclass' : true,
47614
47615         /**
47616          * @event render
47617          * Fires when the grid is rendered
47618          * @param {Grid} grid
47619          */
47620         'render' : true
47621     });
47622
47623     Roo.grid.Grid.superclass.constructor.call(this);
47624 };
47625 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
47626     
47627     /**
47628      * @cfg {String} ddGroup - drag drop group.
47629      */
47630
47631     /**
47632      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
47633      */
47634     minColumnWidth : 25,
47635
47636     /**
47637      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
47638      * <b>on initial render.</b> It is more efficient to explicitly size the columns
47639      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
47640      */
47641     autoSizeColumns : false,
47642
47643     /**
47644      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
47645      */
47646     autoSizeHeaders : true,
47647
47648     /**
47649      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
47650      */
47651     monitorWindowResize : true,
47652
47653     /**
47654      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
47655      * rows measured to get a columns size. Default is 0 (all rows).
47656      */
47657     maxRowsToMeasure : 0,
47658
47659     /**
47660      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
47661      */
47662     trackMouseOver : true,
47663
47664     /**
47665     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
47666     */
47667     
47668     /**
47669     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
47670     */
47671     enableDragDrop : false,
47672     
47673     /**
47674     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
47675     */
47676     enableColumnMove : true,
47677     
47678     /**
47679     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
47680     */
47681     enableColumnHide : true,
47682     
47683     /**
47684     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
47685     */
47686     enableRowHeightSync : false,
47687     
47688     /**
47689     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
47690     */
47691     stripeRows : true,
47692     
47693     /**
47694     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
47695     */
47696     autoHeight : false,
47697
47698     /**
47699      * @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.
47700      */
47701     autoExpandColumn : false,
47702
47703     /**
47704     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
47705     * Default is 50.
47706     */
47707     autoExpandMin : 50,
47708
47709     /**
47710     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
47711     */
47712     autoExpandMax : 1000,
47713
47714     /**
47715     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
47716     */
47717     view : null,
47718
47719     /**
47720     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
47721     */
47722     loadMask : false,
47723     /**
47724     * @cfg {Roo.dd.DropTarget} dragTarget An {@link Roo.dd.DragTarget} config
47725     */
47726     dropTarget: false,
47727     
47728    
47729     
47730     // private
47731     rendered : false,
47732
47733     /**
47734     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
47735     * of a fixed width. Default is false.
47736     */
47737     /**
47738     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
47739     */
47740     /**
47741      * Called once after all setup has been completed and the grid is ready to be rendered.
47742      * @return {Roo.grid.Grid} this
47743      */
47744     render : function()
47745     {
47746         var c = this.container;
47747         // try to detect autoHeight/width mode
47748         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
47749             this.autoHeight = true;
47750         }
47751         var view = this.getView();
47752         view.init(this);
47753
47754         c.on("click", this.onClick, this);
47755         c.on("dblclick", this.onDblClick, this);
47756         c.on("contextmenu", this.onContextMenu, this);
47757         c.on("keydown", this.onKeyDown, this);
47758
47759         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
47760
47761         this.getSelectionModel().init(this);
47762
47763         view.render();
47764
47765         if(this.loadMask){
47766             this.loadMask = new Roo.LoadMask(this.container,
47767                     Roo.apply({store:this.dataSource}, this.loadMask));
47768         }
47769         
47770         
47771         if (this.toolbar && this.toolbar.xtype) {
47772             this.toolbar.container = this.getView().getHeaderPanel(true);
47773             this.toolbar = new Roo.Toolbar(this.toolbar);
47774         }
47775         if (this.footer && this.footer.xtype) {
47776             this.footer.dataSource = this.getDataSource();
47777             this.footer.container = this.getView().getFooterPanel(true);
47778             this.footer = Roo.factory(this.footer, Roo);
47779         }
47780         if (this.dropTarget && this.dropTarget.xtype) {
47781             delete this.dropTarget.xtype;
47782             this.dropTarget =  new Ext.dd.DropTarget(this.getView().mainBody, this.dropTarget);
47783         }
47784         
47785         
47786         this.rendered = true;
47787         this.fireEvent('render', this);
47788         return this;
47789     },
47790
47791         /**
47792          * Reconfigures the grid to use a different Store and Column Model.
47793          * The View will be bound to the new objects and refreshed.
47794          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
47795          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
47796          */
47797     reconfigure : function(dataSource, colModel){
47798         if(this.loadMask){
47799             this.loadMask.destroy();
47800             this.loadMask = new Roo.LoadMask(this.container,
47801                     Roo.apply({store:dataSource}, this.loadMask));
47802         }
47803         this.view.bind(dataSource, colModel);
47804         this.dataSource = dataSource;
47805         this.colModel = colModel;
47806         this.view.refresh(true);
47807     },
47808
47809     // private
47810     onKeyDown : function(e){
47811         this.fireEvent("keydown", e);
47812     },
47813
47814     /**
47815      * Destroy this grid.
47816      * @param {Boolean} removeEl True to remove the element
47817      */
47818     destroy : function(removeEl, keepListeners){
47819         if(this.loadMask){
47820             this.loadMask.destroy();
47821         }
47822         var c = this.container;
47823         c.removeAllListeners();
47824         this.view.destroy();
47825         this.colModel.purgeListeners();
47826         if(!keepListeners){
47827             this.purgeListeners();
47828         }
47829         c.update("");
47830         if(removeEl === true){
47831             c.remove();
47832         }
47833     },
47834
47835     // private
47836     processEvent : function(name, e){
47837         this.fireEvent(name, e);
47838         var t = e.getTarget();
47839         var v = this.view;
47840         var header = v.findHeaderIndex(t);
47841         if(header !== false){
47842             this.fireEvent("header" + name, this, header, e);
47843         }else{
47844             var row = v.findRowIndex(t);
47845             var cell = v.findCellIndex(t);
47846             if(row !== false){
47847                 this.fireEvent("row" + name, this, row, e);
47848                 if(cell !== false){
47849                     this.fireEvent("cell" + name, this, row, cell, e);
47850                 }
47851             }
47852         }
47853     },
47854
47855     // private
47856     onClick : function(e){
47857         this.processEvent("click", e);
47858     },
47859
47860     // private
47861     onContextMenu : function(e, t){
47862         this.processEvent("contextmenu", e);
47863     },
47864
47865     // private
47866     onDblClick : function(e){
47867         this.processEvent("dblclick", e);
47868     },
47869
47870     // private
47871     walkCells : function(row, col, step, fn, scope){
47872         var cm = this.colModel, clen = cm.getColumnCount();
47873         var ds = this.dataSource, rlen = ds.getCount(), first = true;
47874         if(step < 0){
47875             if(col < 0){
47876                 row--;
47877                 first = false;
47878             }
47879             while(row >= 0){
47880                 if(!first){
47881                     col = clen-1;
47882                 }
47883                 first = false;
47884                 while(col >= 0){
47885                     if(fn.call(scope || this, row, col, cm) === true){
47886                         return [row, col];
47887                     }
47888                     col--;
47889                 }
47890                 row--;
47891             }
47892         } else {
47893             if(col >= clen){
47894                 row++;
47895                 first = false;
47896             }
47897             while(row < rlen){
47898                 if(!first){
47899                     col = 0;
47900                 }
47901                 first = false;
47902                 while(col < clen){
47903                     if(fn.call(scope || this, row, col, cm) === true){
47904                         return [row, col];
47905                     }
47906                     col++;
47907                 }
47908                 row++;
47909             }
47910         }
47911         return null;
47912     },
47913
47914     // private
47915     getSelections : function(){
47916         return this.selModel.getSelections();
47917     },
47918
47919     /**
47920      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
47921      * but if manual update is required this method will initiate it.
47922      */
47923     autoSize : function(){
47924         if(this.rendered){
47925             this.view.layout();
47926             if(this.view.adjustForScroll){
47927                 this.view.adjustForScroll();
47928             }
47929         }
47930     },
47931
47932     /**
47933      * Returns the grid's underlying element.
47934      * @return {Element} The element
47935      */
47936     getGridEl : function(){
47937         return this.container;
47938     },
47939
47940     // private for compatibility, overridden by editor grid
47941     stopEditing : function(){},
47942
47943     /**
47944      * Returns the grid's SelectionModel.
47945      * @return {SelectionModel}
47946      */
47947     getSelectionModel : function(){
47948         if(!this.selModel){
47949             this.selModel = new Roo.grid.RowSelectionModel();
47950         }
47951         return this.selModel;
47952     },
47953
47954     /**
47955      * Returns the grid's DataSource.
47956      * @return {DataSource}
47957      */
47958     getDataSource : function(){
47959         return this.dataSource;
47960     },
47961
47962     /**
47963      * Returns the grid's ColumnModel.
47964      * @return {ColumnModel}
47965      */
47966     getColumnModel : function(){
47967         return this.colModel;
47968     },
47969
47970     /**
47971      * Returns the grid's GridView object.
47972      * @return {GridView}
47973      */
47974     getView : function(){
47975         if(!this.view){
47976             this.view = new Roo.grid.GridView(this.viewConfig);
47977         }
47978         return this.view;
47979     },
47980     /**
47981      * Called to get grid's drag proxy text, by default returns this.ddText.
47982      * @return {String}
47983      */
47984     getDragDropText : function(){
47985         var count = this.selModel.getCount();
47986         return String.format(this.ddText, count, count == 1 ? '' : 's');
47987     }
47988 });
47989 /**
47990  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
47991  * %0 is replaced with the number of selected rows.
47992  * @type String
47993  */
47994 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
47995  * Based on:
47996  * Ext JS Library 1.1.1
47997  * Copyright(c) 2006-2007, Ext JS, LLC.
47998  *
47999  * Originally Released Under LGPL - original licence link has changed is not relivant.
48000  *
48001  * Fork - LGPL
48002  * <script type="text/javascript">
48003  */
48004  
48005 Roo.grid.AbstractGridView = function(){
48006         this.grid = null;
48007         
48008         this.events = {
48009             "beforerowremoved" : true,
48010             "beforerowsinserted" : true,
48011             "beforerefresh" : true,
48012             "rowremoved" : true,
48013             "rowsinserted" : true,
48014             "rowupdated" : true,
48015             "refresh" : true
48016         };
48017     Roo.grid.AbstractGridView.superclass.constructor.call(this);
48018 };
48019
48020 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
48021     rowClass : "x-grid-row",
48022     cellClass : "x-grid-cell",
48023     tdClass : "x-grid-td",
48024     hdClass : "x-grid-hd",
48025     splitClass : "x-grid-hd-split",
48026     
48027         init: function(grid){
48028         this.grid = grid;
48029                 var cid = this.grid.getGridEl().id;
48030         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
48031         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
48032         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
48033         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
48034         },
48035         
48036         getColumnRenderers : function(){
48037         var renderers = [];
48038         var cm = this.grid.colModel;
48039         var colCount = cm.getColumnCount();
48040         for(var i = 0; i < colCount; i++){
48041             renderers[i] = cm.getRenderer(i);
48042         }
48043         return renderers;
48044     },
48045     
48046     getColumnIds : function(){
48047         var ids = [];
48048         var cm = this.grid.colModel;
48049         var colCount = cm.getColumnCount();
48050         for(var i = 0; i < colCount; i++){
48051             ids[i] = cm.getColumnId(i);
48052         }
48053         return ids;
48054     },
48055     
48056     getDataIndexes : function(){
48057         if(!this.indexMap){
48058             this.indexMap = this.buildIndexMap();
48059         }
48060         return this.indexMap.colToData;
48061     },
48062     
48063     getColumnIndexByDataIndex : function(dataIndex){
48064         if(!this.indexMap){
48065             this.indexMap = this.buildIndexMap();
48066         }
48067         return this.indexMap.dataToCol[dataIndex];
48068     },
48069     
48070     /**
48071      * Set a css style for a column dynamically. 
48072      * @param {Number} colIndex The index of the column
48073      * @param {String} name The css property name
48074      * @param {String} value The css value
48075      */
48076     setCSSStyle : function(colIndex, name, value){
48077         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
48078         Roo.util.CSS.updateRule(selector, name, value);
48079     },
48080     
48081     generateRules : function(cm){
48082         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
48083         Roo.util.CSS.removeStyleSheet(rulesId);
48084         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48085             var cid = cm.getColumnId(i);
48086             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
48087                          this.tdSelector, cid, " {\n}\n",
48088                          this.hdSelector, cid, " {\n}\n",
48089                          this.splitSelector, cid, " {\n}\n");
48090         }
48091         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
48092     }
48093 });/*
48094  * Based on:
48095  * Ext JS Library 1.1.1
48096  * Copyright(c) 2006-2007, Ext JS, LLC.
48097  *
48098  * Originally Released Under LGPL - original licence link has changed is not relivant.
48099  *
48100  * Fork - LGPL
48101  * <script type="text/javascript">
48102  */
48103
48104 // private
48105 // This is a support class used internally by the Grid components
48106 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
48107     this.grid = grid;
48108     this.view = grid.getView();
48109     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
48110     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
48111     if(hd2){
48112         this.setHandleElId(Roo.id(hd));
48113         this.setOuterHandleElId(Roo.id(hd2));
48114     }
48115     this.scroll = false;
48116 };
48117 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
48118     maxDragWidth: 120,
48119     getDragData : function(e){
48120         var t = Roo.lib.Event.getTarget(e);
48121         var h = this.view.findHeaderCell(t);
48122         if(h){
48123             return {ddel: h.firstChild, header:h};
48124         }
48125         return false;
48126     },
48127
48128     onInitDrag : function(e){
48129         this.view.headersDisabled = true;
48130         var clone = this.dragData.ddel.cloneNode(true);
48131         clone.id = Roo.id();
48132         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
48133         this.proxy.update(clone);
48134         return true;
48135     },
48136
48137     afterValidDrop : function(){
48138         var v = this.view;
48139         setTimeout(function(){
48140             v.headersDisabled = false;
48141         }, 50);
48142     },
48143
48144     afterInvalidDrop : function(){
48145         var v = this.view;
48146         setTimeout(function(){
48147             v.headersDisabled = false;
48148         }, 50);
48149     }
48150 });
48151 /*
48152  * Based on:
48153  * Ext JS Library 1.1.1
48154  * Copyright(c) 2006-2007, Ext JS, LLC.
48155  *
48156  * Originally Released Under LGPL - original licence link has changed is not relivant.
48157  *
48158  * Fork - LGPL
48159  * <script type="text/javascript">
48160  */
48161 // private
48162 // This is a support class used internally by the Grid components
48163 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
48164     this.grid = grid;
48165     this.view = grid.getView();
48166     // split the proxies so they don't interfere with mouse events
48167     this.proxyTop = Roo.DomHelper.append(document.body, {
48168         cls:"col-move-top", html:"&#160;"
48169     }, true);
48170     this.proxyBottom = Roo.DomHelper.append(document.body, {
48171         cls:"col-move-bottom", html:"&#160;"
48172     }, true);
48173     this.proxyTop.hide = this.proxyBottom.hide = function(){
48174         this.setLeftTop(-100,-100);
48175         this.setStyle("visibility", "hidden");
48176     };
48177     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
48178     // temporarily disabled
48179     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
48180     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
48181 };
48182 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
48183     proxyOffsets : [-4, -9],
48184     fly: Roo.Element.fly,
48185
48186     getTargetFromEvent : function(e){
48187         var t = Roo.lib.Event.getTarget(e);
48188         var cindex = this.view.findCellIndex(t);
48189         if(cindex !== false){
48190             return this.view.getHeaderCell(cindex);
48191         }
48192         return null;
48193     },
48194
48195     nextVisible : function(h){
48196         var v = this.view, cm = this.grid.colModel;
48197         h = h.nextSibling;
48198         while(h){
48199             if(!cm.isHidden(v.getCellIndex(h))){
48200                 return h;
48201             }
48202             h = h.nextSibling;
48203         }
48204         return null;
48205     },
48206
48207     prevVisible : function(h){
48208         var v = this.view, cm = this.grid.colModel;
48209         h = h.prevSibling;
48210         while(h){
48211             if(!cm.isHidden(v.getCellIndex(h))){
48212                 return h;
48213             }
48214             h = h.prevSibling;
48215         }
48216         return null;
48217     },
48218
48219     positionIndicator : function(h, n, e){
48220         var x = Roo.lib.Event.getPageX(e);
48221         var r = Roo.lib.Dom.getRegion(n.firstChild);
48222         var px, pt, py = r.top + this.proxyOffsets[1];
48223         if((r.right - x) <= (r.right-r.left)/2){
48224             px = r.right+this.view.borderWidth;
48225             pt = "after";
48226         }else{
48227             px = r.left;
48228             pt = "before";
48229         }
48230         var oldIndex = this.view.getCellIndex(h);
48231         var newIndex = this.view.getCellIndex(n);
48232
48233         if(this.grid.colModel.isFixed(newIndex)){
48234             return false;
48235         }
48236
48237         var locked = this.grid.colModel.isLocked(newIndex);
48238
48239         if(pt == "after"){
48240             newIndex++;
48241         }
48242         if(oldIndex < newIndex){
48243             newIndex--;
48244         }
48245         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
48246             return false;
48247         }
48248         px +=  this.proxyOffsets[0];
48249         this.proxyTop.setLeftTop(px, py);
48250         this.proxyTop.show();
48251         if(!this.bottomOffset){
48252             this.bottomOffset = this.view.mainHd.getHeight();
48253         }
48254         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
48255         this.proxyBottom.show();
48256         return pt;
48257     },
48258
48259     onNodeEnter : function(n, dd, e, data){
48260         if(data.header != n){
48261             this.positionIndicator(data.header, n, e);
48262         }
48263     },
48264
48265     onNodeOver : function(n, dd, e, data){
48266         var result = false;
48267         if(data.header != n){
48268             result = this.positionIndicator(data.header, n, e);
48269         }
48270         if(!result){
48271             this.proxyTop.hide();
48272             this.proxyBottom.hide();
48273         }
48274         return result ? this.dropAllowed : this.dropNotAllowed;
48275     },
48276
48277     onNodeOut : function(n, dd, e, data){
48278         this.proxyTop.hide();
48279         this.proxyBottom.hide();
48280     },
48281
48282     onNodeDrop : function(n, dd, e, data){
48283         var h = data.header;
48284         if(h != n){
48285             var cm = this.grid.colModel;
48286             var x = Roo.lib.Event.getPageX(e);
48287             var r = Roo.lib.Dom.getRegion(n.firstChild);
48288             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
48289             var oldIndex = this.view.getCellIndex(h);
48290             var newIndex = this.view.getCellIndex(n);
48291             var locked = cm.isLocked(newIndex);
48292             if(pt == "after"){
48293                 newIndex++;
48294             }
48295             if(oldIndex < newIndex){
48296                 newIndex--;
48297             }
48298             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
48299                 return false;
48300             }
48301             cm.setLocked(oldIndex, locked, true);
48302             cm.moveColumn(oldIndex, newIndex);
48303             this.grid.fireEvent("columnmove", oldIndex, newIndex);
48304             return true;
48305         }
48306         return false;
48307     }
48308 });
48309 /*
48310  * Based on:
48311  * Ext JS Library 1.1.1
48312  * Copyright(c) 2006-2007, Ext JS, LLC.
48313  *
48314  * Originally Released Under LGPL - original licence link has changed is not relivant.
48315  *
48316  * Fork - LGPL
48317  * <script type="text/javascript">
48318  */
48319   
48320 /**
48321  * @class Roo.grid.GridView
48322  * @extends Roo.util.Observable
48323  *
48324  * @constructor
48325  * @param {Object} config
48326  */
48327 Roo.grid.GridView = function(config){
48328     Roo.grid.GridView.superclass.constructor.call(this);
48329     this.el = null;
48330
48331     Roo.apply(this, config);
48332 };
48333
48334 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
48335
48336     /**
48337      * Override this function to apply custom css classes to rows during rendering
48338      * @param {Record} record The record
48339      * @param {Number} index
48340      * @method getRowClass
48341      */
48342     rowClass : "x-grid-row",
48343
48344     cellClass : "x-grid-col",
48345
48346     tdClass : "x-grid-td",
48347
48348     hdClass : "x-grid-hd",
48349
48350     splitClass : "x-grid-split",
48351
48352     sortClasses : ["sort-asc", "sort-desc"],
48353
48354     enableMoveAnim : false,
48355
48356     hlColor: "C3DAF9",
48357
48358     dh : Roo.DomHelper,
48359
48360     fly : Roo.Element.fly,
48361
48362     css : Roo.util.CSS,
48363
48364     borderWidth: 1,
48365
48366     splitOffset: 3,
48367
48368     scrollIncrement : 22,
48369
48370     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
48371
48372     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
48373
48374     bind : function(ds, cm){
48375         if(this.ds){
48376             this.ds.un("load", this.onLoad, this);
48377             this.ds.un("datachanged", this.onDataChange, this);
48378             this.ds.un("add", this.onAdd, this);
48379             this.ds.un("remove", this.onRemove, this);
48380             this.ds.un("update", this.onUpdate, this);
48381             this.ds.un("clear", this.onClear, this);
48382         }
48383         if(ds){
48384             ds.on("load", this.onLoad, this);
48385             ds.on("datachanged", this.onDataChange, this);
48386             ds.on("add", this.onAdd, this);
48387             ds.on("remove", this.onRemove, this);
48388             ds.on("update", this.onUpdate, this);
48389             ds.on("clear", this.onClear, this);
48390         }
48391         this.ds = ds;
48392
48393         if(this.cm){
48394             this.cm.un("widthchange", this.onColWidthChange, this);
48395             this.cm.un("headerchange", this.onHeaderChange, this);
48396             this.cm.un("hiddenchange", this.onHiddenChange, this);
48397             this.cm.un("columnmoved", this.onColumnMove, this);
48398             this.cm.un("columnlockchange", this.onColumnLock, this);
48399         }
48400         if(cm){
48401             this.generateRules(cm);
48402             cm.on("widthchange", this.onColWidthChange, this);
48403             cm.on("headerchange", this.onHeaderChange, this);
48404             cm.on("hiddenchange", this.onHiddenChange, this);
48405             cm.on("columnmoved", this.onColumnMove, this);
48406             cm.on("columnlockchange", this.onColumnLock, this);
48407         }
48408         this.cm = cm;
48409     },
48410
48411     init: function(grid){
48412         Roo.grid.GridView.superclass.init.call(this, grid);
48413
48414         this.bind(grid.dataSource, grid.colModel);
48415
48416         grid.on("headerclick", this.handleHeaderClick, this);
48417
48418         if(grid.trackMouseOver){
48419             grid.on("mouseover", this.onRowOver, this);
48420             grid.on("mouseout", this.onRowOut, this);
48421         }
48422         grid.cancelTextSelection = function(){};
48423         this.gridId = grid.id;
48424
48425         var tpls = this.templates || {};
48426
48427         if(!tpls.master){
48428             tpls.master = new Roo.Template(
48429                '<div class="x-grid" hidefocus="true">',
48430                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
48431                   '<div class="x-grid-topbar"></div>',
48432                   '<div class="x-grid-scroller"><div></div></div>',
48433                   '<div class="x-grid-locked">',
48434                       '<div class="x-grid-header">{lockedHeader}</div>',
48435                       '<div class="x-grid-body">{lockedBody}</div>',
48436                   "</div>",
48437                   '<div class="x-grid-viewport">',
48438                       '<div class="x-grid-header">{header}</div>',
48439                       '<div class="x-grid-body">{body}</div>',
48440                   "</div>",
48441                   '<div class="x-grid-bottombar"></div>',
48442                  
48443                   '<div class="x-grid-resize-proxy">&#160;</div>',
48444                "</div>"
48445             );
48446             tpls.master.disableformats = true;
48447         }
48448
48449         if(!tpls.header){
48450             tpls.header = new Roo.Template(
48451                '<table border="0" cellspacing="0" cellpadding="0">',
48452                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
48453                "</table>{splits}"
48454             );
48455             tpls.header.disableformats = true;
48456         }
48457         tpls.header.compile();
48458
48459         if(!tpls.hcell){
48460             tpls.hcell = new Roo.Template(
48461                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
48462                 '<div class="x-grid-hd-text" unselectable="on">{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
48463                 "</div></td>"
48464              );
48465              tpls.hcell.disableFormats = true;
48466         }
48467         tpls.hcell.compile();
48468
48469         if(!tpls.hsplit){
48470             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style}" unselectable="on">&#160;</div>');
48471             tpls.hsplit.disableFormats = true;
48472         }
48473         tpls.hsplit.compile();
48474
48475         if(!tpls.body){
48476             tpls.body = new Roo.Template(
48477                '<table border="0" cellspacing="0" cellpadding="0">',
48478                "<tbody>{rows}</tbody>",
48479                "</table>"
48480             );
48481             tpls.body.disableFormats = true;
48482         }
48483         tpls.body.compile();
48484
48485         if(!tpls.row){
48486             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
48487             tpls.row.disableFormats = true;
48488         }
48489         tpls.row.compile();
48490
48491         if(!tpls.cell){
48492             tpls.cell = new Roo.Template(
48493                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
48494                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text" unselectable="on" {attr}>{value}</div></div>',
48495                 "</td>"
48496             );
48497             tpls.cell.disableFormats = true;
48498         }
48499         tpls.cell.compile();
48500
48501         this.templates = tpls;
48502     },
48503
48504     // remap these for backwards compat
48505     onColWidthChange : function(){
48506         this.updateColumns.apply(this, arguments);
48507     },
48508     onHeaderChange : function(){
48509         this.updateHeaders.apply(this, arguments);
48510     }, 
48511     onHiddenChange : function(){
48512         this.handleHiddenChange.apply(this, arguments);
48513     },
48514     onColumnMove : function(){
48515         this.handleColumnMove.apply(this, arguments);
48516     },
48517     onColumnLock : function(){
48518         this.handleLockChange.apply(this, arguments);
48519     },
48520
48521     onDataChange : function(){
48522         this.refresh();
48523         this.updateHeaderSortState();
48524     },
48525
48526     onClear : function(){
48527         this.refresh();
48528     },
48529
48530     onUpdate : function(ds, record){
48531         this.refreshRow(record);
48532     },
48533
48534     refreshRow : function(record){
48535         var ds = this.ds, index;
48536         if(typeof record == 'number'){
48537             index = record;
48538             record = ds.getAt(index);
48539         }else{
48540             index = ds.indexOf(record);
48541         }
48542         this.insertRows(ds, index, index, true);
48543         this.onRemove(ds, record, index+1, true);
48544         this.syncRowHeights(index, index);
48545         this.layout();
48546         this.fireEvent("rowupdated", this, index, record);
48547     },
48548
48549     onAdd : function(ds, records, index){
48550         this.insertRows(ds, index, index + (records.length-1));
48551     },
48552
48553     onRemove : function(ds, record, index, isUpdate){
48554         if(isUpdate !== true){
48555             this.fireEvent("beforerowremoved", this, index, record);
48556         }
48557         var bt = this.getBodyTable(), lt = this.getLockedTable();
48558         if(bt.rows[index]){
48559             bt.firstChild.removeChild(bt.rows[index]);
48560         }
48561         if(lt.rows[index]){
48562             lt.firstChild.removeChild(lt.rows[index]);
48563         }
48564         if(isUpdate !== true){
48565             this.stripeRows(index);
48566             this.syncRowHeights(index, index);
48567             this.layout();
48568             this.fireEvent("rowremoved", this, index, record);
48569         }
48570     },
48571
48572     onLoad : function(){
48573         this.scrollToTop();
48574     },
48575
48576     /**
48577      * Scrolls the grid to the top
48578      */
48579     scrollToTop : function(){
48580         if(this.scroller){
48581             this.scroller.dom.scrollTop = 0;
48582             this.syncScroll();
48583         }
48584     },
48585
48586     /**
48587      * Gets a panel in the header of the grid that can be used for toolbars etc.
48588      * After modifying the contents of this panel a call to grid.autoSize() may be
48589      * required to register any changes in size.
48590      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
48591      * @return Roo.Element
48592      */
48593     getHeaderPanel : function(doShow){
48594         if(doShow){
48595             this.headerPanel.show();
48596         }
48597         return this.headerPanel;
48598     },
48599
48600     /**
48601      * Gets a panel in the footer of the grid that can be used for toolbars etc.
48602      * After modifying the contents of this panel a call to grid.autoSize() may be
48603      * required to register any changes in size.
48604      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
48605      * @return Roo.Element
48606      */
48607     getFooterPanel : function(doShow){
48608         if(doShow){
48609             this.footerPanel.show();
48610         }
48611         return this.footerPanel;
48612     },
48613
48614     initElements : function(){
48615         var E = Roo.Element;
48616         var el = this.grid.getGridEl().dom.firstChild;
48617         var cs = el.childNodes;
48618
48619         this.el = new E(el);
48620         
48621          this.focusEl = new E(el.firstChild);
48622         this.focusEl.swallowEvent("click", true);
48623         
48624         this.headerPanel = new E(cs[1]);
48625         this.headerPanel.enableDisplayMode("block");
48626
48627         this.scroller = new E(cs[2]);
48628         this.scrollSizer = new E(this.scroller.dom.firstChild);
48629
48630         this.lockedWrap = new E(cs[3]);
48631         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
48632         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
48633
48634         this.mainWrap = new E(cs[4]);
48635         this.mainHd = new E(this.mainWrap.dom.firstChild);
48636         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
48637
48638         this.footerPanel = new E(cs[5]);
48639         this.footerPanel.enableDisplayMode("block");
48640
48641         this.resizeProxy = new E(cs[6]);
48642
48643         this.headerSelector = String.format(
48644            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
48645            this.lockedHd.id, this.mainHd.id
48646         );
48647
48648         this.splitterSelector = String.format(
48649            '#{0} div.x-grid-split, #{1} div.x-grid-split',
48650            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
48651         );
48652     },
48653     idToCssName : function(s)
48654     {
48655         return s.replace(/[^a-z0-9]+/ig, '-');
48656     },
48657
48658     getHeaderCell : function(index){
48659         return Roo.DomQuery.select(this.headerSelector)[index];
48660     },
48661
48662     getHeaderCellMeasure : function(index){
48663         return this.getHeaderCell(index).firstChild;
48664     },
48665
48666     getHeaderCellText : function(index){
48667         return this.getHeaderCell(index).firstChild.firstChild;
48668     },
48669
48670     getLockedTable : function(){
48671         return this.lockedBody.dom.firstChild;
48672     },
48673
48674     getBodyTable : function(){
48675         return this.mainBody.dom.firstChild;
48676     },
48677
48678     getLockedRow : function(index){
48679         return this.getLockedTable().rows[index];
48680     },
48681
48682     getRow : function(index){
48683         return this.getBodyTable().rows[index];
48684     },
48685
48686     getRowComposite : function(index){
48687         if(!this.rowEl){
48688             this.rowEl = new Roo.CompositeElementLite();
48689         }
48690         var els = [], lrow, mrow;
48691         if(lrow = this.getLockedRow(index)){
48692             els.push(lrow);
48693         }
48694         if(mrow = this.getRow(index)){
48695             els.push(mrow);
48696         }
48697         this.rowEl.elements = els;
48698         return this.rowEl;
48699     },
48700     /**
48701      * Gets the 'td' of the cell
48702      * 
48703      * @param {Integer} rowIndex row to select
48704      * @param {Integer} colIndex column to select
48705      * 
48706      * @return {Object} 
48707      */
48708     getCell : function(rowIndex, colIndex){
48709         var locked = this.cm.getLockedCount();
48710         var source;
48711         if(colIndex < locked){
48712             source = this.lockedBody.dom.firstChild;
48713         }else{
48714             source = this.mainBody.dom.firstChild;
48715             colIndex -= locked;
48716         }
48717         return source.rows[rowIndex].childNodes[colIndex];
48718     },
48719
48720     getCellText : function(rowIndex, colIndex){
48721         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
48722     },
48723
48724     getCellBox : function(cell){
48725         var b = this.fly(cell).getBox();
48726         if(Roo.isOpera){ // opera fails to report the Y
48727             b.y = cell.offsetTop + this.mainBody.getY();
48728         }
48729         return b;
48730     },
48731
48732     getCellIndex : function(cell){
48733         var id = String(cell.className).match(this.cellRE);
48734         if(id){
48735             return parseInt(id[1], 10);
48736         }
48737         return 0;
48738     },
48739
48740     findHeaderIndex : function(n){
48741         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
48742         return r ? this.getCellIndex(r) : false;
48743     },
48744
48745     findHeaderCell : function(n){
48746         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
48747         return r ? r : false;
48748     },
48749
48750     findRowIndex : function(n){
48751         if(!n){
48752             return false;
48753         }
48754         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
48755         return r ? r.rowIndex : false;
48756     },
48757
48758     findCellIndex : function(node){
48759         var stop = this.el.dom;
48760         while(node && node != stop){
48761             if(this.findRE.test(node.className)){
48762                 return this.getCellIndex(node);
48763             }
48764             node = node.parentNode;
48765         }
48766         return false;
48767     },
48768
48769     getColumnId : function(index){
48770         return this.cm.getColumnId(index);
48771     },
48772
48773     getSplitters : function()
48774     {
48775         if(this.splitterSelector){
48776            return Roo.DomQuery.select(this.splitterSelector);
48777         }else{
48778             return null;
48779       }
48780     },
48781
48782     getSplitter : function(index){
48783         return this.getSplitters()[index];
48784     },
48785
48786     onRowOver : function(e, t){
48787         var row;
48788         if((row = this.findRowIndex(t)) !== false){
48789             this.getRowComposite(row).addClass("x-grid-row-over");
48790         }
48791     },
48792
48793     onRowOut : function(e, t){
48794         var row;
48795         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
48796             this.getRowComposite(row).removeClass("x-grid-row-over");
48797         }
48798     },
48799
48800     renderHeaders : function(){
48801         var cm = this.cm;
48802         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
48803         var cb = [], lb = [], sb = [], lsb = [], p = {};
48804         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48805             p.cellId = "x-grid-hd-0-" + i;
48806             p.splitId = "x-grid-csplit-0-" + i;
48807             p.id = cm.getColumnId(i);
48808             p.title = cm.getColumnTooltip(i) || "";
48809             p.value = cm.getColumnHeader(i) || "";
48810             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
48811             if(!cm.isLocked(i)){
48812                 cb[cb.length] = ct.apply(p);
48813                 sb[sb.length] = st.apply(p);
48814             }else{
48815                 lb[lb.length] = ct.apply(p);
48816                 lsb[lsb.length] = st.apply(p);
48817             }
48818         }
48819         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
48820                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
48821     },
48822
48823     updateHeaders : function(){
48824         var html = this.renderHeaders();
48825         this.lockedHd.update(html[0]);
48826         this.mainHd.update(html[1]);
48827     },
48828
48829     /**
48830      * Focuses the specified row.
48831      * @param {Number} row The row index
48832      */
48833     focusRow : function(row)
48834     {
48835         //Roo.log('GridView.focusRow');
48836         var x = this.scroller.dom.scrollLeft;
48837         this.focusCell(row, 0, false);
48838         this.scroller.dom.scrollLeft = x;
48839     },
48840
48841     /**
48842      * Focuses the specified cell.
48843      * @param {Number} row The row index
48844      * @param {Number} col The column index
48845      * @param {Boolean} hscroll false to disable horizontal scrolling
48846      */
48847     focusCell : function(row, col, hscroll)
48848     {
48849         //Roo.log('GridView.focusCell');
48850         var el = this.ensureVisible(row, col, hscroll);
48851         this.focusEl.alignTo(el, "tl-tl");
48852         if(Roo.isGecko){
48853             this.focusEl.focus();
48854         }else{
48855             this.focusEl.focus.defer(1, this.focusEl);
48856         }
48857     },
48858
48859     /**
48860      * Scrolls the specified cell into view
48861      * @param {Number} row The row index
48862      * @param {Number} col The column index
48863      * @param {Boolean} hscroll false to disable horizontal scrolling
48864      */
48865     ensureVisible : function(row, col, hscroll)
48866     {
48867         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
48868         //return null; //disable for testing.
48869         if(typeof row != "number"){
48870             row = row.rowIndex;
48871         }
48872         if(row < 0 && row >= this.ds.getCount()){
48873             return  null;
48874         }
48875         col = (col !== undefined ? col : 0);
48876         var cm = this.grid.colModel;
48877         while(cm.isHidden(col)){
48878             col++;
48879         }
48880
48881         var el = this.getCell(row, col);
48882         if(!el){
48883             return null;
48884         }
48885         var c = this.scroller.dom;
48886
48887         var ctop = parseInt(el.offsetTop, 10);
48888         var cleft = parseInt(el.offsetLeft, 10);
48889         var cbot = ctop + el.offsetHeight;
48890         var cright = cleft + el.offsetWidth;
48891         
48892         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
48893         var stop = parseInt(c.scrollTop, 10);
48894         var sleft = parseInt(c.scrollLeft, 10);
48895         var sbot = stop + ch;
48896         var sright = sleft + c.clientWidth;
48897         /*
48898         Roo.log('GridView.ensureVisible:' +
48899                 ' ctop:' + ctop +
48900                 ' c.clientHeight:' + c.clientHeight +
48901                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
48902                 ' stop:' + stop +
48903                 ' cbot:' + cbot +
48904                 ' sbot:' + sbot +
48905                 ' ch:' + ch  
48906                 );
48907         */
48908         if(ctop < stop){
48909              c.scrollTop = ctop;
48910             //Roo.log("set scrolltop to ctop DISABLE?");
48911         }else if(cbot > sbot){
48912             //Roo.log("set scrolltop to cbot-ch");
48913             c.scrollTop = cbot-ch;
48914         }
48915         
48916         if(hscroll !== false){
48917             if(cleft < sleft){
48918                 c.scrollLeft = cleft;
48919             }else if(cright > sright){
48920                 c.scrollLeft = cright-c.clientWidth;
48921             }
48922         }
48923          
48924         return el;
48925     },
48926
48927     updateColumns : function(){
48928         this.grid.stopEditing();
48929         var cm = this.grid.colModel, colIds = this.getColumnIds();
48930         //var totalWidth = cm.getTotalWidth();
48931         var pos = 0;
48932         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48933             //if(cm.isHidden(i)) continue;
48934             var w = cm.getColumnWidth(i);
48935             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
48936             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
48937         }
48938         this.updateSplitters();
48939     },
48940
48941     generateRules : function(cm){
48942         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
48943         Roo.util.CSS.removeStyleSheet(rulesId);
48944         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48945             var cid = cm.getColumnId(i);
48946             var align = '';
48947             if(cm.config[i].align){
48948                 align = 'text-align:'+cm.config[i].align+';';
48949             }
48950             var hidden = '';
48951             if(cm.isHidden(i)){
48952                 hidden = 'display:none;';
48953             }
48954             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
48955             ruleBuf.push(
48956                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
48957                     this.hdSelector, cid, " {\n", align, width, "}\n",
48958                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
48959                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
48960         }
48961         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
48962     },
48963
48964     updateSplitters : function(){
48965         var cm = this.cm, s = this.getSplitters();
48966         if(s){ // splitters not created yet
48967             var pos = 0, locked = true;
48968             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48969                 if(cm.isHidden(i)) continue;
48970                 var w = cm.getColumnWidth(i); // make sure it's a number
48971                 if(!cm.isLocked(i) && locked){
48972                     pos = 0;
48973                     locked = false;
48974                 }
48975                 pos += w;
48976                 s[i].style.left = (pos-this.splitOffset) + "px";
48977             }
48978         }
48979     },
48980
48981     handleHiddenChange : function(colModel, colIndex, hidden){
48982         if(hidden){
48983             this.hideColumn(colIndex);
48984         }else{
48985             this.unhideColumn(colIndex);
48986         }
48987     },
48988
48989     hideColumn : function(colIndex){
48990         var cid = this.getColumnId(colIndex);
48991         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
48992         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
48993         if(Roo.isSafari){
48994             this.updateHeaders();
48995         }
48996         this.updateSplitters();
48997         this.layout();
48998     },
48999
49000     unhideColumn : function(colIndex){
49001         var cid = this.getColumnId(colIndex);
49002         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
49003         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
49004
49005         if(Roo.isSafari){
49006             this.updateHeaders();
49007         }
49008         this.updateSplitters();
49009         this.layout();
49010     },
49011
49012     insertRows : function(dm, firstRow, lastRow, isUpdate){
49013         if(firstRow == 0 && lastRow == dm.getCount()-1){
49014             this.refresh();
49015         }else{
49016             if(!isUpdate){
49017                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
49018             }
49019             var s = this.getScrollState();
49020             var markup = this.renderRows(firstRow, lastRow);
49021             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
49022             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
49023             this.restoreScroll(s);
49024             if(!isUpdate){
49025                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
49026                 this.syncRowHeights(firstRow, lastRow);
49027                 this.stripeRows(firstRow);
49028                 this.layout();
49029             }
49030         }
49031     },
49032
49033     bufferRows : function(markup, target, index){
49034         var before = null, trows = target.rows, tbody = target.tBodies[0];
49035         if(index < trows.length){
49036             before = trows[index];
49037         }
49038         var b = document.createElement("div");
49039         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
49040         var rows = b.firstChild.rows;
49041         for(var i = 0, len = rows.length; i < len; i++){
49042             if(before){
49043                 tbody.insertBefore(rows[0], before);
49044             }else{
49045                 tbody.appendChild(rows[0]);
49046             }
49047         }
49048         b.innerHTML = "";
49049         b = null;
49050     },
49051
49052     deleteRows : function(dm, firstRow, lastRow){
49053         if(dm.getRowCount()<1){
49054             this.fireEvent("beforerefresh", this);
49055             this.mainBody.update("");
49056             this.lockedBody.update("");
49057             this.fireEvent("refresh", this);
49058         }else{
49059             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
49060             var bt = this.getBodyTable();
49061             var tbody = bt.firstChild;
49062             var rows = bt.rows;
49063             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
49064                 tbody.removeChild(rows[firstRow]);
49065             }
49066             this.stripeRows(firstRow);
49067             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
49068         }
49069     },
49070
49071     updateRows : function(dataSource, firstRow, lastRow){
49072         var s = this.getScrollState();
49073         this.refresh();
49074         this.restoreScroll(s);
49075     },
49076
49077     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
49078         if(!noRefresh){
49079            this.refresh();
49080         }
49081         this.updateHeaderSortState();
49082     },
49083
49084     getScrollState : function(){
49085         
49086         var sb = this.scroller.dom;
49087         return {left: sb.scrollLeft, top: sb.scrollTop};
49088     },
49089
49090     stripeRows : function(startRow){
49091         if(!this.grid.stripeRows || this.ds.getCount() < 1){
49092             return;
49093         }
49094         startRow = startRow || 0;
49095         var rows = this.getBodyTable().rows;
49096         var lrows = this.getLockedTable().rows;
49097         var cls = ' x-grid-row-alt ';
49098         for(var i = startRow, len = rows.length; i < len; i++){
49099             var row = rows[i], lrow = lrows[i];
49100             var isAlt = ((i+1) % 2 == 0);
49101             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
49102             if(isAlt == hasAlt){
49103                 continue;
49104             }
49105             if(isAlt){
49106                 row.className += " x-grid-row-alt";
49107             }else{
49108                 row.className = row.className.replace("x-grid-row-alt", "");
49109             }
49110             if(lrow){
49111                 lrow.className = row.className;
49112             }
49113         }
49114     },
49115
49116     restoreScroll : function(state){
49117         //Roo.log('GridView.restoreScroll');
49118         var sb = this.scroller.dom;
49119         sb.scrollLeft = state.left;
49120         sb.scrollTop = state.top;
49121         this.syncScroll();
49122     },
49123
49124     syncScroll : function(){
49125         //Roo.log('GridView.syncScroll');
49126         var sb = this.scroller.dom;
49127         var sh = this.mainHd.dom;
49128         var bs = this.mainBody.dom;
49129         var lv = this.lockedBody.dom;
49130         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
49131         lv.scrollTop = bs.scrollTop = sb.scrollTop;
49132     },
49133
49134     handleScroll : function(e){
49135         this.syncScroll();
49136         var sb = this.scroller.dom;
49137         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
49138         e.stopEvent();
49139     },
49140
49141     handleWheel : function(e){
49142         var d = e.getWheelDelta();
49143         this.scroller.dom.scrollTop -= d*22;
49144         // set this here to prevent jumpy scrolling on large tables
49145         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
49146         e.stopEvent();
49147     },
49148
49149     renderRows : function(startRow, endRow){
49150         // pull in all the crap needed to render rows
49151         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
49152         var colCount = cm.getColumnCount();
49153
49154         if(ds.getCount() < 1){
49155             return ["", ""];
49156         }
49157
49158         // build a map for all the columns
49159         var cs = [];
49160         for(var i = 0; i < colCount; i++){
49161             var name = cm.getDataIndex(i);
49162             cs[i] = {
49163                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
49164                 renderer : cm.getRenderer(i),
49165                 id : cm.getColumnId(i),
49166                 locked : cm.isLocked(i)
49167             };
49168         }
49169
49170         startRow = startRow || 0;
49171         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
49172
49173         // records to render
49174         var rs = ds.getRange(startRow, endRow);
49175
49176         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
49177     },
49178
49179     // As much as I hate to duplicate code, this was branched because FireFox really hates
49180     // [].join("") on strings. The performance difference was substantial enough to
49181     // branch this function
49182     doRender : Roo.isGecko ?
49183             function(cs, rs, ds, startRow, colCount, stripe){
49184                 var ts = this.templates, ct = ts.cell, rt = ts.row;
49185                 // buffers
49186                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
49187                 
49188                 var hasListener = this.grid.hasListener('rowclass');
49189                 var rowcfg = {};
49190                 for(var j = 0, len = rs.length; j < len; j++){
49191                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
49192                     for(var i = 0; i < colCount; i++){
49193                         c = cs[i];
49194                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
49195                         p.id = c.id;
49196                         p.css = p.attr = "";
49197                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
49198                         if(p.value == undefined || p.value === "") p.value = "&#160;";
49199                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
49200                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
49201                         }
49202                         var markup = ct.apply(p);
49203                         if(!c.locked){
49204                             cb+= markup;
49205                         }else{
49206                             lcb+= markup;
49207                         }
49208                     }
49209                     var alt = [];
49210                     if(stripe && ((rowIndex+1) % 2 == 0)){
49211                         alt.push("x-grid-row-alt")
49212                     }
49213                     if(r.dirty){
49214                         alt.push(  " x-grid-dirty-row");
49215                     }
49216                     rp.cells = lcb;
49217                     if(this.getRowClass){
49218                         alt.push(this.getRowClass(r, rowIndex));
49219                     }
49220                     if (hasListener) {
49221                         rowcfg = {
49222                              
49223                             record: r,
49224                             rowIndex : rowIndex,
49225                             rowClass : ''
49226                         }
49227                         this.grid.fireEvent('rowclass', this, rowcfg);
49228                         alt.push(rowcfg.rowClass);
49229                     }
49230                     rp.alt = alt.join(" ");
49231                     lbuf+= rt.apply(rp);
49232                     rp.cells = cb;
49233                     buf+=  rt.apply(rp);
49234                 }
49235                 return [lbuf, buf];
49236             } :
49237             function(cs, rs, ds, startRow, colCount, stripe){
49238                 var ts = this.templates, ct = ts.cell, rt = ts.row;
49239                 // buffers
49240                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
49241                 var hasListener = this.grid.hasListener('rowclass');
49242                 var rowcfg = {};
49243                 for(var j = 0, len = rs.length; j < len; j++){
49244                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
49245                     for(var i = 0; i < colCount; i++){
49246                         c = cs[i];
49247                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
49248                         p.id = c.id;
49249                         p.css = p.attr = "";
49250                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
49251                         if(p.value == undefined || p.value === "") p.value = "&#160;";
49252                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
49253                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
49254                         }
49255                         var markup = ct.apply(p);
49256                         if(!c.locked){
49257                             cb[cb.length] = markup;
49258                         }else{
49259                             lcb[lcb.length] = markup;
49260                         }
49261                     }
49262                     var alt = [];
49263                     if(stripe && ((rowIndex+1) % 2 == 0)){
49264                         alt.push( "x-grid-row-alt");
49265                     }
49266                     if(r.dirty){
49267                         alt.push(" x-grid-dirty-row");
49268                     }
49269                     rp.cells = lcb;
49270                     if(this.getRowClass){
49271                         alt.push( this.getRowClass(r, rowIndex));
49272                     }
49273                     if (hasListener) {
49274                         rowcfg = {
49275                              
49276                             record: r,
49277                             rowIndex : rowIndex,
49278                             rowClass : ''
49279                         }
49280                         this.grid.fireEvent('rowclass', this, rowcfg);
49281                         alt.push(rowcfg.rowClass);
49282                     }
49283                     rp.alt = alt.join(" ");
49284                     rp.cells = lcb.join("");
49285                     lbuf[lbuf.length] = rt.apply(rp);
49286                     rp.cells = cb.join("");
49287                     buf[buf.length] =  rt.apply(rp);
49288                 }
49289                 return [lbuf.join(""), buf.join("")];
49290             },
49291
49292     renderBody : function(){
49293         var markup = this.renderRows();
49294         var bt = this.templates.body;
49295         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
49296     },
49297
49298     /**
49299      * Refreshes the grid
49300      * @param {Boolean} headersToo
49301      */
49302     refresh : function(headersToo){
49303         this.fireEvent("beforerefresh", this);
49304         this.grid.stopEditing();
49305         var result = this.renderBody();
49306         this.lockedBody.update(result[0]);
49307         this.mainBody.update(result[1]);
49308         if(headersToo === true){
49309             this.updateHeaders();
49310             this.updateColumns();
49311             this.updateSplitters();
49312             this.updateHeaderSortState();
49313         }
49314         this.syncRowHeights();
49315         this.layout();
49316         this.fireEvent("refresh", this);
49317     },
49318
49319     handleColumnMove : function(cm, oldIndex, newIndex){
49320         this.indexMap = null;
49321         var s = this.getScrollState();
49322         this.refresh(true);
49323         this.restoreScroll(s);
49324         this.afterMove(newIndex);
49325     },
49326
49327     afterMove : function(colIndex){
49328         if(this.enableMoveAnim && Roo.enableFx){
49329             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
49330         }
49331         // if multisort - fix sortOrder, and reload..
49332         if (this.grid.dataSource.multiSort) {
49333             // the we can call sort again..
49334             var dm = this.grid.dataSource;
49335             var cm = this.grid.colModel;
49336             var so = [];
49337             for(var i = 0; i < cm.config.length; i++ ) {
49338                 
49339                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
49340                     continue; // dont' bother, it's not in sort list or being set.
49341                 }
49342                 
49343                 so.push(cm.config[i].dataIndex);
49344             };
49345             dm.sortOrder = so;
49346             dm.load(dm.lastOptions);
49347             
49348             
49349         }
49350         
49351     },
49352
49353     updateCell : function(dm, rowIndex, dataIndex){
49354         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
49355         if(typeof colIndex == "undefined"){ // not present in grid
49356             return;
49357         }
49358         var cm = this.grid.colModel;
49359         var cell = this.getCell(rowIndex, colIndex);
49360         var cellText = this.getCellText(rowIndex, colIndex);
49361
49362         var p = {
49363             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
49364             id : cm.getColumnId(colIndex),
49365             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
49366         };
49367         var renderer = cm.getRenderer(colIndex);
49368         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
49369         if(typeof val == "undefined" || val === "") val = "&#160;";
49370         cellText.innerHTML = val;
49371         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
49372         this.syncRowHeights(rowIndex, rowIndex);
49373     },
49374
49375     calcColumnWidth : function(colIndex, maxRowsToMeasure){
49376         var maxWidth = 0;
49377         if(this.grid.autoSizeHeaders){
49378             var h = this.getHeaderCellMeasure(colIndex);
49379             maxWidth = Math.max(maxWidth, h.scrollWidth);
49380         }
49381         var tb, index;
49382         if(this.cm.isLocked(colIndex)){
49383             tb = this.getLockedTable();
49384             index = colIndex;
49385         }else{
49386             tb = this.getBodyTable();
49387             index = colIndex - this.cm.getLockedCount();
49388         }
49389         if(tb && tb.rows){
49390             var rows = tb.rows;
49391             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
49392             for(var i = 0; i < stopIndex; i++){
49393                 var cell = rows[i].childNodes[index].firstChild;
49394                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
49395             }
49396         }
49397         return maxWidth + /*margin for error in IE*/ 5;
49398     },
49399     /**
49400      * Autofit a column to its content.
49401      * @param {Number} colIndex
49402      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
49403      */
49404      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
49405          if(this.cm.isHidden(colIndex)){
49406              return; // can't calc a hidden column
49407          }
49408         if(forceMinSize){
49409             var cid = this.cm.getColumnId(colIndex);
49410             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
49411            if(this.grid.autoSizeHeaders){
49412                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
49413            }
49414         }
49415         var newWidth = this.calcColumnWidth(colIndex);
49416         this.cm.setColumnWidth(colIndex,
49417             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
49418         if(!suppressEvent){
49419             this.grid.fireEvent("columnresize", colIndex, newWidth);
49420         }
49421     },
49422
49423     /**
49424      * Autofits all columns to their content and then expands to fit any extra space in the grid
49425      */
49426      autoSizeColumns : function(){
49427         var cm = this.grid.colModel;
49428         var colCount = cm.getColumnCount();
49429         for(var i = 0; i < colCount; i++){
49430             this.autoSizeColumn(i, true, true);
49431         }
49432         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
49433             this.fitColumns();
49434         }else{
49435             this.updateColumns();
49436             this.layout();
49437         }
49438     },
49439
49440     /**
49441      * Autofits all columns to the grid's width proportionate with their current size
49442      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
49443      */
49444     fitColumns : function(reserveScrollSpace){
49445         var cm = this.grid.colModel;
49446         var colCount = cm.getColumnCount();
49447         var cols = [];
49448         var width = 0;
49449         var i, w;
49450         for (i = 0; i < colCount; i++){
49451             if(!cm.isHidden(i) && !cm.isFixed(i)){
49452                 w = cm.getColumnWidth(i);
49453                 cols.push(i);
49454                 cols.push(w);
49455                 width += w;
49456             }
49457         }
49458         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
49459         if(reserveScrollSpace){
49460             avail -= 17;
49461         }
49462         var frac = (avail - cm.getTotalWidth())/width;
49463         while (cols.length){
49464             w = cols.pop();
49465             i = cols.pop();
49466             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
49467         }
49468         this.updateColumns();
49469         this.layout();
49470     },
49471
49472     onRowSelect : function(rowIndex){
49473         var row = this.getRowComposite(rowIndex);
49474         row.addClass("x-grid-row-selected");
49475     },
49476
49477     onRowDeselect : function(rowIndex){
49478         var row = this.getRowComposite(rowIndex);
49479         row.removeClass("x-grid-row-selected");
49480     },
49481
49482     onCellSelect : function(row, col){
49483         var cell = this.getCell(row, col);
49484         if(cell){
49485             Roo.fly(cell).addClass("x-grid-cell-selected");
49486         }
49487     },
49488
49489     onCellDeselect : function(row, col){
49490         var cell = this.getCell(row, col);
49491         if(cell){
49492             Roo.fly(cell).removeClass("x-grid-cell-selected");
49493         }
49494     },
49495
49496     updateHeaderSortState : function(){
49497         
49498         // sort state can be single { field: xxx, direction : yyy}
49499         // or   { xxx=>ASC , yyy : DESC ..... }
49500         
49501         var mstate = {};
49502         if (!this.ds.multiSort) { 
49503             var state = this.ds.getSortState();
49504             if(!state){
49505                 return;
49506             }
49507             mstate[state.field] = state.direction;
49508             // FIXME... - this is not used here.. but might be elsewhere..
49509             this.sortState = state;
49510             
49511         } else {
49512             mstate = this.ds.sortToggle;
49513         }
49514         //remove existing sort classes..
49515         
49516         var sc = this.sortClasses;
49517         var hds = this.el.select(this.headerSelector).removeClass(sc);
49518         
49519         for(var f in mstate) {
49520         
49521             var sortColumn = this.cm.findColumnIndex(f);
49522             
49523             if(sortColumn != -1){
49524                 var sortDir = mstate[f];        
49525                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
49526             }
49527         }
49528         
49529          
49530         
49531     },
49532
49533
49534     handleHeaderClick : function(g, index){
49535         if(this.headersDisabled){
49536             return;
49537         }
49538         var dm = g.dataSource, cm = g.colModel;
49539         if(!cm.isSortable(index)){
49540             return;
49541         }
49542         g.stopEditing();
49543         
49544         if (dm.multiSort) {
49545             // update the sortOrder
49546             var so = [];
49547             for(var i = 0; i < cm.config.length; i++ ) {
49548                 
49549                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
49550                     continue; // dont' bother, it's not in sort list or being set.
49551                 }
49552                 
49553                 so.push(cm.config[i].dataIndex);
49554             };
49555             dm.sortOrder = so;
49556         }
49557         
49558         
49559         dm.sort(cm.getDataIndex(index));
49560     },
49561
49562
49563     destroy : function(){
49564         if(this.colMenu){
49565             this.colMenu.removeAll();
49566             Roo.menu.MenuMgr.unregister(this.colMenu);
49567             this.colMenu.getEl().remove();
49568             delete this.colMenu;
49569         }
49570         if(this.hmenu){
49571             this.hmenu.removeAll();
49572             Roo.menu.MenuMgr.unregister(this.hmenu);
49573             this.hmenu.getEl().remove();
49574             delete this.hmenu;
49575         }
49576         if(this.grid.enableColumnMove){
49577             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
49578             if(dds){
49579                 for(var dd in dds){
49580                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
49581                         var elid = dds[dd].dragElId;
49582                         dds[dd].unreg();
49583                         Roo.get(elid).remove();
49584                     } else if(dds[dd].config.isTarget){
49585                         dds[dd].proxyTop.remove();
49586                         dds[dd].proxyBottom.remove();
49587                         dds[dd].unreg();
49588                     }
49589                     if(Roo.dd.DDM.locationCache[dd]){
49590                         delete Roo.dd.DDM.locationCache[dd];
49591                     }
49592                 }
49593                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
49594             }
49595         }
49596         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
49597         this.bind(null, null);
49598         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
49599     },
49600
49601     handleLockChange : function(){
49602         this.refresh(true);
49603     },
49604
49605     onDenyColumnLock : function(){
49606
49607     },
49608
49609     onDenyColumnHide : function(){
49610
49611     },
49612
49613     handleHdMenuClick : function(item){
49614         var index = this.hdCtxIndex;
49615         var cm = this.cm, ds = this.ds;
49616         switch(item.id){
49617             case "asc":
49618                 ds.sort(cm.getDataIndex(index), "ASC");
49619                 break;
49620             case "desc":
49621                 ds.sort(cm.getDataIndex(index), "DESC");
49622                 break;
49623             case "lock":
49624                 var lc = cm.getLockedCount();
49625                 if(cm.getColumnCount(true) <= lc+1){
49626                     this.onDenyColumnLock();
49627                     return;
49628                 }
49629                 if(lc != index){
49630                     cm.setLocked(index, true, true);
49631                     cm.moveColumn(index, lc);
49632                     this.grid.fireEvent("columnmove", index, lc);
49633                 }else{
49634                     cm.setLocked(index, true);
49635                 }
49636             break;
49637             case "unlock":
49638                 var lc = cm.getLockedCount();
49639                 if((lc-1) != index){
49640                     cm.setLocked(index, false, true);
49641                     cm.moveColumn(index, lc-1);
49642                     this.grid.fireEvent("columnmove", index, lc-1);
49643                 }else{
49644                     cm.setLocked(index, false);
49645                 }
49646             break;
49647             default:
49648                 index = cm.getIndexById(item.id.substr(4));
49649                 if(index != -1){
49650                     if(item.checked && cm.getColumnCount(true) <= 1){
49651                         this.onDenyColumnHide();
49652                         return false;
49653                     }
49654                     cm.setHidden(index, item.checked);
49655                 }
49656         }
49657         return true;
49658     },
49659
49660     beforeColMenuShow : function(){
49661         var cm = this.cm,  colCount = cm.getColumnCount();
49662         this.colMenu.removeAll();
49663         for(var i = 0; i < colCount; i++){
49664             this.colMenu.add(new Roo.menu.CheckItem({
49665                 id: "col-"+cm.getColumnId(i),
49666                 text: cm.getColumnHeader(i),
49667                 checked: !cm.isHidden(i),
49668                 hideOnClick:false
49669             }));
49670         }
49671     },
49672
49673     handleHdCtx : function(g, index, e){
49674         e.stopEvent();
49675         var hd = this.getHeaderCell(index);
49676         this.hdCtxIndex = index;
49677         var ms = this.hmenu.items, cm = this.cm;
49678         ms.get("asc").setDisabled(!cm.isSortable(index));
49679         ms.get("desc").setDisabled(!cm.isSortable(index));
49680         if(this.grid.enableColLock !== false){
49681             ms.get("lock").setDisabled(cm.isLocked(index));
49682             ms.get("unlock").setDisabled(!cm.isLocked(index));
49683         }
49684         this.hmenu.show(hd, "tl-bl");
49685     },
49686
49687     handleHdOver : function(e){
49688         var hd = this.findHeaderCell(e.getTarget());
49689         if(hd && !this.headersDisabled){
49690             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
49691                this.fly(hd).addClass("x-grid-hd-over");
49692             }
49693         }
49694     },
49695
49696     handleHdOut : function(e){
49697         var hd = this.findHeaderCell(e.getTarget());
49698         if(hd){
49699             this.fly(hd).removeClass("x-grid-hd-over");
49700         }
49701     },
49702
49703     handleSplitDblClick : function(e, t){
49704         var i = this.getCellIndex(t);
49705         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
49706             this.autoSizeColumn(i, true);
49707             this.layout();
49708         }
49709     },
49710
49711     render : function(){
49712
49713         var cm = this.cm;
49714         var colCount = cm.getColumnCount();
49715
49716         if(this.grid.monitorWindowResize === true){
49717             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
49718         }
49719         var header = this.renderHeaders();
49720         var body = this.templates.body.apply({rows:""});
49721         var html = this.templates.master.apply({
49722             lockedBody: body,
49723             body: body,
49724             lockedHeader: header[0],
49725             header: header[1]
49726         });
49727
49728         //this.updateColumns();
49729
49730         this.grid.getGridEl().dom.innerHTML = html;
49731
49732         this.initElements();
49733         
49734         // a kludge to fix the random scolling effect in webkit
49735         this.el.on("scroll", function() {
49736             this.el.dom.scrollTop=0; // hopefully not recursive..
49737         },this);
49738
49739         this.scroller.on("scroll", this.handleScroll, this);
49740         this.lockedBody.on("mousewheel", this.handleWheel, this);
49741         this.mainBody.on("mousewheel", this.handleWheel, this);
49742
49743         this.mainHd.on("mouseover", this.handleHdOver, this);
49744         this.mainHd.on("mouseout", this.handleHdOut, this);
49745         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
49746                 {delegate: "."+this.splitClass});
49747
49748         this.lockedHd.on("mouseover", this.handleHdOver, this);
49749         this.lockedHd.on("mouseout", this.handleHdOut, this);
49750         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
49751                 {delegate: "."+this.splitClass});
49752
49753         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
49754             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
49755         }
49756
49757         this.updateSplitters();
49758
49759         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
49760             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
49761             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
49762         }
49763
49764         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
49765             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
49766             this.hmenu.add(
49767                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
49768                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
49769             );
49770             if(this.grid.enableColLock !== false){
49771                 this.hmenu.add('-',
49772                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
49773                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
49774                 );
49775             }
49776             if(this.grid.enableColumnHide !== false){
49777
49778                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
49779                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
49780                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
49781
49782                 this.hmenu.add('-',
49783                     {id:"columns", text: this.columnsText, menu: this.colMenu}
49784                 );
49785             }
49786             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
49787
49788             this.grid.on("headercontextmenu", this.handleHdCtx, this);
49789         }
49790
49791         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
49792             this.dd = new Roo.grid.GridDragZone(this.grid, {
49793                 ddGroup : this.grid.ddGroup || 'GridDD'
49794             });
49795         }
49796
49797         /*
49798         for(var i = 0; i < colCount; i++){
49799             if(cm.isHidden(i)){
49800                 this.hideColumn(i);
49801             }
49802             if(cm.config[i].align){
49803                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
49804                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
49805             }
49806         }*/
49807         
49808         this.updateHeaderSortState();
49809
49810         this.beforeInitialResize();
49811         this.layout(true);
49812
49813         // two part rendering gives faster view to the user
49814         this.renderPhase2.defer(1, this);
49815     },
49816
49817     renderPhase2 : function(){
49818         // render the rows now
49819         this.refresh();
49820         if(this.grid.autoSizeColumns){
49821             this.autoSizeColumns();
49822         }
49823     },
49824
49825     beforeInitialResize : function(){
49826
49827     },
49828
49829     onColumnSplitterMoved : function(i, w){
49830         this.userResized = true;
49831         var cm = this.grid.colModel;
49832         cm.setColumnWidth(i, w, true);
49833         var cid = cm.getColumnId(i);
49834         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
49835         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
49836         this.updateSplitters();
49837         this.layout();
49838         this.grid.fireEvent("columnresize", i, w);
49839     },
49840
49841     syncRowHeights : function(startIndex, endIndex){
49842         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
49843             startIndex = startIndex || 0;
49844             var mrows = this.getBodyTable().rows;
49845             var lrows = this.getLockedTable().rows;
49846             var len = mrows.length-1;
49847             endIndex = Math.min(endIndex || len, len);
49848             for(var i = startIndex; i <= endIndex; i++){
49849                 var m = mrows[i], l = lrows[i];
49850                 var h = Math.max(m.offsetHeight, l.offsetHeight);
49851                 m.style.height = l.style.height = h + "px";
49852             }
49853         }
49854     },
49855
49856     layout : function(initialRender, is2ndPass){
49857         var g = this.grid;
49858         var auto = g.autoHeight;
49859         var scrollOffset = 16;
49860         var c = g.getGridEl(), cm = this.cm,
49861                 expandCol = g.autoExpandColumn,
49862                 gv = this;
49863         //c.beginMeasure();
49864
49865         if(!c.dom.offsetWidth){ // display:none?
49866             if(initialRender){
49867                 this.lockedWrap.show();
49868                 this.mainWrap.show();
49869             }
49870             return;
49871         }
49872
49873         var hasLock = this.cm.isLocked(0);
49874
49875         var tbh = this.headerPanel.getHeight();
49876         var bbh = this.footerPanel.getHeight();
49877
49878         if(auto){
49879             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
49880             var newHeight = ch + c.getBorderWidth("tb");
49881             if(g.maxHeight){
49882                 newHeight = Math.min(g.maxHeight, newHeight);
49883             }
49884             c.setHeight(newHeight);
49885         }
49886
49887         if(g.autoWidth){
49888             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
49889         }
49890
49891         var s = this.scroller;
49892
49893         var csize = c.getSize(true);
49894
49895         this.el.setSize(csize.width, csize.height);
49896
49897         this.headerPanel.setWidth(csize.width);
49898         this.footerPanel.setWidth(csize.width);
49899
49900         var hdHeight = this.mainHd.getHeight();
49901         var vw = csize.width;
49902         var vh = csize.height - (tbh + bbh);
49903
49904         s.setSize(vw, vh);
49905
49906         var bt = this.getBodyTable();
49907         var ltWidth = hasLock ?
49908                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
49909
49910         var scrollHeight = bt.offsetHeight;
49911         var scrollWidth = ltWidth + bt.offsetWidth;
49912         var vscroll = false, hscroll = false;
49913
49914         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
49915
49916         var lw = this.lockedWrap, mw = this.mainWrap;
49917         var lb = this.lockedBody, mb = this.mainBody;
49918
49919         setTimeout(function(){
49920             var t = s.dom.offsetTop;
49921             var w = s.dom.clientWidth,
49922                 h = s.dom.clientHeight;
49923
49924             lw.setTop(t);
49925             lw.setSize(ltWidth, h);
49926
49927             mw.setLeftTop(ltWidth, t);
49928             mw.setSize(w-ltWidth, h);
49929
49930             lb.setHeight(h-hdHeight);
49931             mb.setHeight(h-hdHeight);
49932
49933             if(is2ndPass !== true && !gv.userResized && expandCol){
49934                 // high speed resize without full column calculation
49935                 
49936                 var ci = cm.getIndexById(expandCol);
49937                 if (ci < 0) {
49938                     ci = cm.findColumnIndex(expandCol);
49939                 }
49940                 ci = Math.max(0, ci); // make sure it's got at least the first col.
49941                 var expandId = cm.getColumnId(ci);
49942                 var  tw = cm.getTotalWidth(false);
49943                 var currentWidth = cm.getColumnWidth(ci);
49944                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
49945                 if(currentWidth != cw){
49946                     cm.setColumnWidth(ci, cw, true);
49947                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
49948                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
49949                     gv.updateSplitters();
49950                     gv.layout(false, true);
49951                 }
49952             }
49953
49954             if(initialRender){
49955                 lw.show();
49956                 mw.show();
49957             }
49958             //c.endMeasure();
49959         }, 10);
49960     },
49961
49962     onWindowResize : function(){
49963         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
49964             return;
49965         }
49966         this.layout();
49967     },
49968
49969     appendFooter : function(parentEl){
49970         return null;
49971     },
49972
49973     sortAscText : "Sort Ascending",
49974     sortDescText : "Sort Descending",
49975     lockText : "Lock Column",
49976     unlockText : "Unlock Column",
49977     columnsText : "Columns"
49978 });
49979
49980
49981 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
49982     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
49983     this.proxy.el.addClass('x-grid3-col-dd');
49984 };
49985
49986 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
49987     handleMouseDown : function(e){
49988
49989     },
49990
49991     callHandleMouseDown : function(e){
49992         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
49993     }
49994 });
49995 /*
49996  * Based on:
49997  * Ext JS Library 1.1.1
49998  * Copyright(c) 2006-2007, Ext JS, LLC.
49999  *
50000  * Originally Released Under LGPL - original licence link has changed is not relivant.
50001  *
50002  * Fork - LGPL
50003  * <script type="text/javascript">
50004  */
50005  
50006 // private
50007 // This is a support class used internally by the Grid components
50008 Roo.grid.SplitDragZone = function(grid, hd, hd2){
50009     this.grid = grid;
50010     this.view = grid.getView();
50011     this.proxy = this.view.resizeProxy;
50012     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
50013         "gridSplitters" + this.grid.getGridEl().id, {
50014         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
50015     });
50016     this.setHandleElId(Roo.id(hd));
50017     this.setOuterHandleElId(Roo.id(hd2));
50018     this.scroll = false;
50019 };
50020 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
50021     fly: Roo.Element.fly,
50022
50023     b4StartDrag : function(x, y){
50024         this.view.headersDisabled = true;
50025         this.proxy.setHeight(this.view.mainWrap.getHeight());
50026         var w = this.cm.getColumnWidth(this.cellIndex);
50027         var minw = Math.max(w-this.grid.minColumnWidth, 0);
50028         this.resetConstraints();
50029         this.setXConstraint(minw, 1000);
50030         this.setYConstraint(0, 0);
50031         this.minX = x - minw;
50032         this.maxX = x + 1000;
50033         this.startPos = x;
50034         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
50035     },
50036
50037
50038     handleMouseDown : function(e){
50039         ev = Roo.EventObject.setEvent(e);
50040         var t = this.fly(ev.getTarget());
50041         if(t.hasClass("x-grid-split")){
50042             this.cellIndex = this.view.getCellIndex(t.dom);
50043             this.split = t.dom;
50044             this.cm = this.grid.colModel;
50045             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
50046                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
50047             }
50048         }
50049     },
50050
50051     endDrag : function(e){
50052         this.view.headersDisabled = false;
50053         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
50054         var diff = endX - this.startPos;
50055         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
50056     },
50057
50058     autoOffset : function(){
50059         this.setDelta(0,0);
50060     }
50061 });/*
50062  * Based on:
50063  * Ext JS Library 1.1.1
50064  * Copyright(c) 2006-2007, Ext JS, LLC.
50065  *
50066  * Originally Released Under LGPL - original licence link has changed is not relivant.
50067  *
50068  * Fork - LGPL
50069  * <script type="text/javascript">
50070  */
50071  
50072 // private
50073 // This is a support class used internally by the Grid components
50074 Roo.grid.GridDragZone = function(grid, config){
50075     this.view = grid.getView();
50076     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
50077     if(this.view.lockedBody){
50078         this.setHandleElId(Roo.id(this.view.mainBody.dom));
50079         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
50080     }
50081     this.scroll = false;
50082     this.grid = grid;
50083     this.ddel = document.createElement('div');
50084     this.ddel.className = 'x-grid-dd-wrap';
50085 };
50086
50087 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
50088     ddGroup : "GridDD",
50089
50090     getDragData : function(e){
50091         var t = Roo.lib.Event.getTarget(e);
50092         var rowIndex = this.view.findRowIndex(t);
50093         if(rowIndex !== false){
50094             var sm = this.grid.selModel;
50095             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
50096               //  sm.mouseDown(e, t);
50097             //}
50098             if (e.hasModifier()){
50099                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
50100             }
50101             return {grid: this.grid, ddel: this.ddel, rowIndex: rowIndex, selections:sm.getSelections()};
50102         }
50103         return false;
50104     },
50105
50106     onInitDrag : function(e){
50107         var data = this.dragData;
50108         this.ddel.innerHTML = this.grid.getDragDropText();
50109         this.proxy.update(this.ddel);
50110         // fire start drag?
50111     },
50112
50113     afterRepair : function(){
50114         this.dragging = false;
50115     },
50116
50117     getRepairXY : function(e, data){
50118         return false;
50119     },
50120
50121     onEndDrag : function(data, e){
50122         // fire end drag?
50123     },
50124
50125     onValidDrop : function(dd, e, id){
50126         // fire drag drop?
50127         this.hideProxy();
50128     },
50129
50130     beforeInvalidDrop : function(e, id){
50131
50132     }
50133 });/*
50134  * Based on:
50135  * Ext JS Library 1.1.1
50136  * Copyright(c) 2006-2007, Ext JS, LLC.
50137  *
50138  * Originally Released Under LGPL - original licence link has changed is not relivant.
50139  *
50140  * Fork - LGPL
50141  * <script type="text/javascript">
50142  */
50143  
50144
50145 /**
50146  * @class Roo.grid.ColumnModel
50147  * @extends Roo.util.Observable
50148  * This is the default implementation of a ColumnModel used by the Grid. It defines
50149  * the columns in the grid.
50150  * <br>Usage:<br>
50151  <pre><code>
50152  var colModel = new Roo.grid.ColumnModel([
50153         {header: "Ticker", width: 60, sortable: true, locked: true},
50154         {header: "Company Name", width: 150, sortable: true},
50155         {header: "Market Cap.", width: 100, sortable: true},
50156         {header: "$ Sales", width: 100, sortable: true, renderer: money},
50157         {header: "Employees", width: 100, sortable: true, resizable: false}
50158  ]);
50159  </code></pre>
50160  * <p>
50161  
50162  * The config options listed for this class are options which may appear in each
50163  * individual column definition.
50164  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
50165  * @constructor
50166  * @param {Object} config An Array of column config objects. See this class's
50167  * config objects for details.
50168 */
50169 Roo.grid.ColumnModel = function(config){
50170         /**
50171      * The config passed into the constructor
50172      */
50173     this.config = config;
50174     this.lookup = {};
50175
50176     // if no id, create one
50177     // if the column does not have a dataIndex mapping,
50178     // map it to the order it is in the config
50179     for(var i = 0, len = config.length; i < len; i++){
50180         var c = config[i];
50181         if(typeof c.dataIndex == "undefined"){
50182             c.dataIndex = i;
50183         }
50184         if(typeof c.renderer == "string"){
50185             c.renderer = Roo.util.Format[c.renderer];
50186         }
50187         if(typeof c.id == "undefined"){
50188             c.id = Roo.id();
50189         }
50190         if(c.editor && c.editor.xtype){
50191             c.editor  = Roo.factory(c.editor, Roo.grid);
50192         }
50193         if(c.editor && c.editor.isFormField){
50194             c.editor = new Roo.grid.GridEditor(c.editor);
50195         }
50196         this.lookup[c.id] = c;
50197     }
50198
50199     /**
50200      * The width of columns which have no width specified (defaults to 100)
50201      * @type Number
50202      */
50203     this.defaultWidth = 100;
50204
50205     /**
50206      * Default sortable of columns which have no sortable specified (defaults to false)
50207      * @type Boolean
50208      */
50209     this.defaultSortable = false;
50210
50211     this.addEvents({
50212         /**
50213              * @event widthchange
50214              * Fires when the width of a column changes.
50215              * @param {ColumnModel} this
50216              * @param {Number} columnIndex The column index
50217              * @param {Number} newWidth The new width
50218              */
50219             "widthchange": true,
50220         /**
50221              * @event headerchange
50222              * Fires when the text of a header changes.
50223              * @param {ColumnModel} this
50224              * @param {Number} columnIndex The column index
50225              * @param {Number} newText The new header text
50226              */
50227             "headerchange": true,
50228         /**
50229              * @event hiddenchange
50230              * Fires when a column is hidden or "unhidden".
50231              * @param {ColumnModel} this
50232              * @param {Number} columnIndex The column index
50233              * @param {Boolean} hidden true if hidden, false otherwise
50234              */
50235             "hiddenchange": true,
50236             /**
50237          * @event columnmoved
50238          * Fires when a column is moved.
50239          * @param {ColumnModel} this
50240          * @param {Number} oldIndex
50241          * @param {Number} newIndex
50242          */
50243         "columnmoved" : true,
50244         /**
50245          * @event columlockchange
50246          * Fires when a column's locked state is changed
50247          * @param {ColumnModel} this
50248          * @param {Number} colIndex
50249          * @param {Boolean} locked true if locked
50250          */
50251         "columnlockchange" : true
50252     });
50253     Roo.grid.ColumnModel.superclass.constructor.call(this);
50254 };
50255 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
50256     /**
50257      * @cfg {String} header The header text to display in the Grid view.
50258      */
50259     /**
50260      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
50261      * {@link Roo.data.Record} definition from which to draw the column's value. If not
50262      * specified, the column's index is used as an index into the Record's data Array.
50263      */
50264     /**
50265      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
50266      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
50267      */
50268     /**
50269      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
50270      * Defaults to the value of the {@link #defaultSortable} property.
50271      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
50272      */
50273     /**
50274      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
50275      */
50276     /**
50277      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
50278      */
50279     /**
50280      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
50281      */
50282     /**
50283      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
50284      */
50285     /**
50286      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
50287      * given the cell's data value. See {@link #setRenderer}. If not specified, the
50288      * default renderer uses the raw data value.
50289      */
50290        /**
50291      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
50292      */
50293     /**
50294      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
50295      */
50296
50297     /**
50298      * Returns the id of the column at the specified index.
50299      * @param {Number} index The column index
50300      * @return {String} the id
50301      */
50302     getColumnId : function(index){
50303         return this.config[index].id;
50304     },
50305
50306     /**
50307      * Returns the column for a specified id.
50308      * @param {String} id The column id
50309      * @return {Object} the column
50310      */
50311     getColumnById : function(id){
50312         return this.lookup[id];
50313     },
50314
50315     
50316     /**
50317      * Returns the column for a specified dataIndex.
50318      * @param {String} dataIndex The column dataIndex
50319      * @return {Object|Boolean} the column or false if not found
50320      */
50321     getColumnByDataIndex: function(dataIndex){
50322         var index = this.findColumnIndex(dataIndex);
50323         return index > -1 ? this.config[index] : false;
50324     },
50325     
50326     /**
50327      * Returns the index for a specified column id.
50328      * @param {String} id The column id
50329      * @return {Number} the index, or -1 if not found
50330      */
50331     getIndexById : function(id){
50332         for(var i = 0, len = this.config.length; i < len; i++){
50333             if(this.config[i].id == id){
50334                 return i;
50335             }
50336         }
50337         return -1;
50338     },
50339     
50340     /**
50341      * Returns the index for a specified column dataIndex.
50342      * @param {String} dataIndex The column dataIndex
50343      * @return {Number} the index, or -1 if not found
50344      */
50345     
50346     findColumnIndex : function(dataIndex){
50347         for(var i = 0, len = this.config.length; i < len; i++){
50348             if(this.config[i].dataIndex == dataIndex){
50349                 return i;
50350             }
50351         }
50352         return -1;
50353     },
50354     
50355     
50356     moveColumn : function(oldIndex, newIndex){
50357         var c = this.config[oldIndex];
50358         this.config.splice(oldIndex, 1);
50359         this.config.splice(newIndex, 0, c);
50360         this.dataMap = null;
50361         this.fireEvent("columnmoved", this, oldIndex, newIndex);
50362     },
50363
50364     isLocked : function(colIndex){
50365         return this.config[colIndex].locked === true;
50366     },
50367
50368     setLocked : function(colIndex, value, suppressEvent){
50369         if(this.isLocked(colIndex) == value){
50370             return;
50371         }
50372         this.config[colIndex].locked = value;
50373         if(!suppressEvent){
50374             this.fireEvent("columnlockchange", this, colIndex, value);
50375         }
50376     },
50377
50378     getTotalLockedWidth : function(){
50379         var totalWidth = 0;
50380         for(var i = 0; i < this.config.length; i++){
50381             if(this.isLocked(i) && !this.isHidden(i)){
50382                 this.totalWidth += this.getColumnWidth(i);
50383             }
50384         }
50385         return totalWidth;
50386     },
50387
50388     getLockedCount : function(){
50389         for(var i = 0, len = this.config.length; i < len; i++){
50390             if(!this.isLocked(i)){
50391                 return i;
50392             }
50393         }
50394     },
50395
50396     /**
50397      * Returns the number of columns.
50398      * @return {Number}
50399      */
50400     getColumnCount : function(visibleOnly){
50401         if(visibleOnly === true){
50402             var c = 0;
50403             for(var i = 0, len = this.config.length; i < len; i++){
50404                 if(!this.isHidden(i)){
50405                     c++;
50406                 }
50407             }
50408             return c;
50409         }
50410         return this.config.length;
50411     },
50412
50413     /**
50414      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
50415      * @param {Function} fn
50416      * @param {Object} scope (optional)
50417      * @return {Array} result
50418      */
50419     getColumnsBy : function(fn, scope){
50420         var r = [];
50421         for(var i = 0, len = this.config.length; i < len; i++){
50422             var c = this.config[i];
50423             if(fn.call(scope||this, c, i) === true){
50424                 r[r.length] = c;
50425             }
50426         }
50427         return r;
50428     },
50429
50430     /**
50431      * Returns true if the specified column is sortable.
50432      * @param {Number} col The column index
50433      * @return {Boolean}
50434      */
50435     isSortable : function(col){
50436         if(typeof this.config[col].sortable == "undefined"){
50437             return this.defaultSortable;
50438         }
50439         return this.config[col].sortable;
50440     },
50441
50442     /**
50443      * Returns the rendering (formatting) function defined for the column.
50444      * @param {Number} col The column index.
50445      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
50446      */
50447     getRenderer : function(col){
50448         if(!this.config[col].renderer){
50449             return Roo.grid.ColumnModel.defaultRenderer;
50450         }
50451         return this.config[col].renderer;
50452     },
50453
50454     /**
50455      * Sets the rendering (formatting) function for a column.
50456      * @param {Number} col The column index
50457      * @param {Function} fn The function to use to process the cell's raw data
50458      * to return HTML markup for the grid view. The render function is called with
50459      * the following parameters:<ul>
50460      * <li>Data value.</li>
50461      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
50462      * <li>css A CSS style string to apply to the table cell.</li>
50463      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
50464      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
50465      * <li>Row index</li>
50466      * <li>Column index</li>
50467      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
50468      */
50469     setRenderer : function(col, fn){
50470         this.config[col].renderer = fn;
50471     },
50472
50473     /**
50474      * Returns the width for the specified column.
50475      * @param {Number} col The column index
50476      * @return {Number}
50477      */
50478     getColumnWidth : function(col){
50479         return this.config[col].width * 1 || this.defaultWidth;
50480     },
50481
50482     /**
50483      * Sets the width for a column.
50484      * @param {Number} col The column index
50485      * @param {Number} width The new width
50486      */
50487     setColumnWidth : function(col, width, suppressEvent){
50488         this.config[col].width = width;
50489         this.totalWidth = null;
50490         if(!suppressEvent){
50491              this.fireEvent("widthchange", this, col, width);
50492         }
50493     },
50494
50495     /**
50496      * Returns the total width of all columns.
50497      * @param {Boolean} includeHidden True to include hidden column widths
50498      * @return {Number}
50499      */
50500     getTotalWidth : function(includeHidden){
50501         if(!this.totalWidth){
50502             this.totalWidth = 0;
50503             for(var i = 0, len = this.config.length; i < len; i++){
50504                 if(includeHidden || !this.isHidden(i)){
50505                     this.totalWidth += this.getColumnWidth(i);
50506                 }
50507             }
50508         }
50509         return this.totalWidth;
50510     },
50511
50512     /**
50513      * Returns the header for the specified column.
50514      * @param {Number} col The column index
50515      * @return {String}
50516      */
50517     getColumnHeader : function(col){
50518         return this.config[col].header;
50519     },
50520
50521     /**
50522      * Sets the header for a column.
50523      * @param {Number} col The column index
50524      * @param {String} header The new header
50525      */
50526     setColumnHeader : function(col, header){
50527         this.config[col].header = header;
50528         this.fireEvent("headerchange", this, col, header);
50529     },
50530
50531     /**
50532      * Returns the tooltip for the specified column.
50533      * @param {Number} col The column index
50534      * @return {String}
50535      */
50536     getColumnTooltip : function(col){
50537             return this.config[col].tooltip;
50538     },
50539     /**
50540      * Sets the tooltip for a column.
50541      * @param {Number} col The column index
50542      * @param {String} tooltip The new tooltip
50543      */
50544     setColumnTooltip : function(col, tooltip){
50545             this.config[col].tooltip = tooltip;
50546     },
50547
50548     /**
50549      * Returns the dataIndex for the specified column.
50550      * @param {Number} col The column index
50551      * @return {Number}
50552      */
50553     getDataIndex : function(col){
50554         return this.config[col].dataIndex;
50555     },
50556
50557     /**
50558      * Sets the dataIndex for a column.
50559      * @param {Number} col The column index
50560      * @param {Number} dataIndex The new dataIndex
50561      */
50562     setDataIndex : function(col, dataIndex){
50563         this.config[col].dataIndex = dataIndex;
50564     },
50565
50566     
50567     
50568     /**
50569      * Returns true if the cell is editable.
50570      * @param {Number} colIndex The column index
50571      * @param {Number} rowIndex The row index
50572      * @return {Boolean}
50573      */
50574     isCellEditable : function(colIndex, rowIndex){
50575         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
50576     },
50577
50578     /**
50579      * Returns the editor defined for the cell/column.
50580      * return false or null to disable editing.
50581      * @param {Number} colIndex The column index
50582      * @param {Number} rowIndex The row index
50583      * @return {Object}
50584      */
50585     getCellEditor : function(colIndex, rowIndex){
50586         return this.config[colIndex].editor;
50587     },
50588
50589     /**
50590      * Sets if a column is editable.
50591      * @param {Number} col The column index
50592      * @param {Boolean} editable True if the column is editable
50593      */
50594     setEditable : function(col, editable){
50595         this.config[col].editable = editable;
50596     },
50597
50598
50599     /**
50600      * Returns true if the column is hidden.
50601      * @param {Number} colIndex The column index
50602      * @return {Boolean}
50603      */
50604     isHidden : function(colIndex){
50605         return this.config[colIndex].hidden;
50606     },
50607
50608
50609     /**
50610      * Returns true if the column width cannot be changed
50611      */
50612     isFixed : function(colIndex){
50613         return this.config[colIndex].fixed;
50614     },
50615
50616     /**
50617      * Returns true if the column can be resized
50618      * @return {Boolean}
50619      */
50620     isResizable : function(colIndex){
50621         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
50622     },
50623     /**
50624      * Sets if a column is hidden.
50625      * @param {Number} colIndex The column index
50626      * @param {Boolean} hidden True if the column is hidden
50627      */
50628     setHidden : function(colIndex, hidden){
50629         this.config[colIndex].hidden = hidden;
50630         this.totalWidth = null;
50631         this.fireEvent("hiddenchange", this, colIndex, hidden);
50632     },
50633
50634     /**
50635      * Sets the editor for a column.
50636      * @param {Number} col The column index
50637      * @param {Object} editor The editor object
50638      */
50639     setEditor : function(col, editor){
50640         this.config[col].editor = editor;
50641     }
50642 });
50643
50644 Roo.grid.ColumnModel.defaultRenderer = function(value){
50645         if(typeof value == "string" && value.length < 1){
50646             return "&#160;";
50647         }
50648         return value;
50649 };
50650
50651 // Alias for backwards compatibility
50652 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
50653 /*
50654  * Based on:
50655  * Ext JS Library 1.1.1
50656  * Copyright(c) 2006-2007, Ext JS, LLC.
50657  *
50658  * Originally Released Under LGPL - original licence link has changed is not relivant.
50659  *
50660  * Fork - LGPL
50661  * <script type="text/javascript">
50662  */
50663
50664 /**
50665  * @class Roo.grid.AbstractSelectionModel
50666  * @extends Roo.util.Observable
50667  * Abstract base class for grid SelectionModels.  It provides the interface that should be
50668  * implemented by descendant classes.  This class should not be directly instantiated.
50669  * @constructor
50670  */
50671 Roo.grid.AbstractSelectionModel = function(){
50672     this.locked = false;
50673     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
50674 };
50675
50676 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
50677     /** @ignore Called by the grid automatically. Do not call directly. */
50678     init : function(grid){
50679         this.grid = grid;
50680         this.initEvents();
50681     },
50682
50683     /**
50684      * Locks the selections.
50685      */
50686     lock : function(){
50687         this.locked = true;
50688     },
50689
50690     /**
50691      * Unlocks the selections.
50692      */
50693     unlock : function(){
50694         this.locked = false;
50695     },
50696
50697     /**
50698      * Returns true if the selections are locked.
50699      * @return {Boolean}
50700      */
50701     isLocked : function(){
50702         return this.locked;
50703     }
50704 });/*
50705  * Based on:
50706  * Ext JS Library 1.1.1
50707  * Copyright(c) 2006-2007, Ext JS, LLC.
50708  *
50709  * Originally Released Under LGPL - original licence link has changed is not relivant.
50710  *
50711  * Fork - LGPL
50712  * <script type="text/javascript">
50713  */
50714 /**
50715  * @extends Roo.grid.AbstractSelectionModel
50716  * @class Roo.grid.RowSelectionModel
50717  * The default SelectionModel used by {@link Roo.grid.Grid}.
50718  * It supports multiple selections and keyboard selection/navigation. 
50719  * @constructor
50720  * @param {Object} config
50721  */
50722 Roo.grid.RowSelectionModel = function(config){
50723     Roo.apply(this, config);
50724     this.selections = new Roo.util.MixedCollection(false, function(o){
50725         return o.id;
50726     });
50727
50728     this.last = false;
50729     this.lastActive = false;
50730
50731     this.addEvents({
50732         /**
50733              * @event selectionchange
50734              * Fires when the selection changes
50735              * @param {SelectionModel} this
50736              */
50737             "selectionchange" : true,
50738         /**
50739              * @event afterselectionchange
50740              * Fires after the selection changes (eg. by key press or clicking)
50741              * @param {SelectionModel} this
50742              */
50743             "afterselectionchange" : true,
50744         /**
50745              * @event beforerowselect
50746              * Fires when a row is selected being selected, return false to cancel.
50747              * @param {SelectionModel} this
50748              * @param {Number} rowIndex The selected index
50749              * @param {Boolean} keepExisting False if other selections will be cleared
50750              */
50751             "beforerowselect" : true,
50752         /**
50753              * @event rowselect
50754              * Fires when a row is selected.
50755              * @param {SelectionModel} this
50756              * @param {Number} rowIndex The selected index
50757              * @param {Roo.data.Record} r The record
50758              */
50759             "rowselect" : true,
50760         /**
50761              * @event rowdeselect
50762              * Fires when a row is deselected.
50763              * @param {SelectionModel} this
50764              * @param {Number} rowIndex The selected index
50765              */
50766         "rowdeselect" : true
50767     });
50768     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
50769     this.locked = false;
50770 };
50771
50772 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
50773     /**
50774      * @cfg {Boolean} singleSelect
50775      * True to allow selection of only one row at a time (defaults to false)
50776      */
50777     singleSelect : false,
50778
50779     // private
50780     initEvents : function(){
50781
50782         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
50783             this.grid.on("mousedown", this.handleMouseDown, this);
50784         }else{ // allow click to work like normal
50785             this.grid.on("rowclick", this.handleDragableRowClick, this);
50786         }
50787
50788         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
50789             "up" : function(e){
50790                 if(!e.shiftKey){
50791                     this.selectPrevious(e.shiftKey);
50792                 }else if(this.last !== false && this.lastActive !== false){
50793                     var last = this.last;
50794                     this.selectRange(this.last,  this.lastActive-1);
50795                     this.grid.getView().focusRow(this.lastActive);
50796                     if(last !== false){
50797                         this.last = last;
50798                     }
50799                 }else{
50800                     this.selectFirstRow();
50801                 }
50802                 this.fireEvent("afterselectionchange", this);
50803             },
50804             "down" : function(e){
50805                 if(!e.shiftKey){
50806                     this.selectNext(e.shiftKey);
50807                 }else if(this.last !== false && this.lastActive !== false){
50808                     var last = this.last;
50809                     this.selectRange(this.last,  this.lastActive+1);
50810                     this.grid.getView().focusRow(this.lastActive);
50811                     if(last !== false){
50812                         this.last = last;
50813                     }
50814                 }else{
50815                     this.selectFirstRow();
50816                 }
50817                 this.fireEvent("afterselectionchange", this);
50818             },
50819             scope: this
50820         });
50821
50822         var view = this.grid.view;
50823         view.on("refresh", this.onRefresh, this);
50824         view.on("rowupdated", this.onRowUpdated, this);
50825         view.on("rowremoved", this.onRemove, this);
50826     },
50827
50828     // private
50829     onRefresh : function(){
50830         var ds = this.grid.dataSource, i, v = this.grid.view;
50831         var s = this.selections;
50832         s.each(function(r){
50833             if((i = ds.indexOfId(r.id)) != -1){
50834                 v.onRowSelect(i);
50835             }else{
50836                 s.remove(r);
50837             }
50838         });
50839     },
50840
50841     // private
50842     onRemove : function(v, index, r){
50843         this.selections.remove(r);
50844     },
50845
50846     // private
50847     onRowUpdated : function(v, index, r){
50848         if(this.isSelected(r)){
50849             v.onRowSelect(index);
50850         }
50851     },
50852
50853     /**
50854      * Select records.
50855      * @param {Array} records The records to select
50856      * @param {Boolean} keepExisting (optional) True to keep existing selections
50857      */
50858     selectRecords : function(records, keepExisting){
50859         if(!keepExisting){
50860             this.clearSelections();
50861         }
50862         var ds = this.grid.dataSource;
50863         for(var i = 0, len = records.length; i < len; i++){
50864             this.selectRow(ds.indexOf(records[i]), true);
50865         }
50866     },
50867
50868     /**
50869      * Gets the number of selected rows.
50870      * @return {Number}
50871      */
50872     getCount : function(){
50873         return this.selections.length;
50874     },
50875
50876     /**
50877      * Selects the first row in the grid.
50878      */
50879     selectFirstRow : function(){
50880         this.selectRow(0);
50881     },
50882
50883     /**
50884      * Select the last row.
50885      * @param {Boolean} keepExisting (optional) True to keep existing selections
50886      */
50887     selectLastRow : function(keepExisting){
50888         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
50889     },
50890
50891     /**
50892      * Selects the row immediately following the last selected row.
50893      * @param {Boolean} keepExisting (optional) True to keep existing selections
50894      */
50895     selectNext : function(keepExisting){
50896         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
50897             this.selectRow(this.last+1, keepExisting);
50898             this.grid.getView().focusRow(this.last);
50899         }
50900     },
50901
50902     /**
50903      * Selects the row that precedes the last selected row.
50904      * @param {Boolean} keepExisting (optional) True to keep existing selections
50905      */
50906     selectPrevious : function(keepExisting){
50907         if(this.last){
50908             this.selectRow(this.last-1, keepExisting);
50909             this.grid.getView().focusRow(this.last);
50910         }
50911     },
50912
50913     /**
50914      * Returns the selected records
50915      * @return {Array} Array of selected records
50916      */
50917     getSelections : function(){
50918         return [].concat(this.selections.items);
50919     },
50920
50921     /**
50922      * Returns the first selected record.
50923      * @return {Record}
50924      */
50925     getSelected : function(){
50926         return this.selections.itemAt(0);
50927     },
50928
50929
50930     /**
50931      * Clears all selections.
50932      */
50933     clearSelections : function(fast){
50934         if(this.locked) return;
50935         if(fast !== true){
50936             var ds = this.grid.dataSource;
50937             var s = this.selections;
50938             s.each(function(r){
50939                 this.deselectRow(ds.indexOfId(r.id));
50940             }, this);
50941             s.clear();
50942         }else{
50943             this.selections.clear();
50944         }
50945         this.last = false;
50946     },
50947
50948
50949     /**
50950      * Selects all rows.
50951      */
50952     selectAll : function(){
50953         if(this.locked) return;
50954         this.selections.clear();
50955         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
50956             this.selectRow(i, true);
50957         }
50958     },
50959
50960     /**
50961      * Returns True if there is a selection.
50962      * @return {Boolean}
50963      */
50964     hasSelection : function(){
50965         return this.selections.length > 0;
50966     },
50967
50968     /**
50969      * Returns True if the specified row is selected.
50970      * @param {Number/Record} record The record or index of the record to check
50971      * @return {Boolean}
50972      */
50973     isSelected : function(index){
50974         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
50975         return (r && this.selections.key(r.id) ? true : false);
50976     },
50977
50978     /**
50979      * Returns True if the specified record id is selected.
50980      * @param {String} id The id of record to check
50981      * @return {Boolean}
50982      */
50983     isIdSelected : function(id){
50984         return (this.selections.key(id) ? true : false);
50985     },
50986
50987     // private
50988     handleMouseDown : function(e, t){
50989         var view = this.grid.getView(), rowIndex;
50990         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
50991             return;
50992         };
50993         if(e.shiftKey && this.last !== false){
50994             var last = this.last;
50995             this.selectRange(last, rowIndex, e.ctrlKey);
50996             this.last = last; // reset the last
50997             view.focusRow(rowIndex);
50998         }else{
50999             var isSelected = this.isSelected(rowIndex);
51000             if(e.button !== 0 && isSelected){
51001                 view.focusRow(rowIndex);
51002             }else if(e.ctrlKey && isSelected){
51003                 this.deselectRow(rowIndex);
51004             }else if(!isSelected){
51005                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
51006                 view.focusRow(rowIndex);
51007             }
51008         }
51009         this.fireEvent("afterselectionchange", this);
51010     },
51011     // private
51012     handleDragableRowClick :  function(grid, rowIndex, e) 
51013     {
51014         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
51015             this.selectRow(rowIndex, false);
51016             grid.view.focusRow(rowIndex);
51017              this.fireEvent("afterselectionchange", this);
51018         }
51019     },
51020     
51021     /**
51022      * Selects multiple rows.
51023      * @param {Array} rows Array of the indexes of the row to select
51024      * @param {Boolean} keepExisting (optional) True to keep existing selections
51025      */
51026     selectRows : function(rows, keepExisting){
51027         if(!keepExisting){
51028             this.clearSelections();
51029         }
51030         for(var i = 0, len = rows.length; i < len; i++){
51031             this.selectRow(rows[i], true);
51032         }
51033     },
51034
51035     /**
51036      * Selects a range of rows. All rows in between startRow and endRow are also selected.
51037      * @param {Number} startRow The index of the first row in the range
51038      * @param {Number} endRow The index of the last row in the range
51039      * @param {Boolean} keepExisting (optional) True to retain existing selections
51040      */
51041     selectRange : function(startRow, endRow, keepExisting){
51042         if(this.locked) return;
51043         if(!keepExisting){
51044             this.clearSelections();
51045         }
51046         if(startRow <= endRow){
51047             for(var i = startRow; i <= endRow; i++){
51048                 this.selectRow(i, true);
51049             }
51050         }else{
51051             for(var i = startRow; i >= endRow; i--){
51052                 this.selectRow(i, true);
51053             }
51054         }
51055     },
51056
51057     /**
51058      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
51059      * @param {Number} startRow The index of the first row in the range
51060      * @param {Number} endRow The index of the last row in the range
51061      */
51062     deselectRange : function(startRow, endRow, preventViewNotify){
51063         if(this.locked) return;
51064         for(var i = startRow; i <= endRow; i++){
51065             this.deselectRow(i, preventViewNotify);
51066         }
51067     },
51068
51069     /**
51070      * Selects a row.
51071      * @param {Number} row The index of the row to select
51072      * @param {Boolean} keepExisting (optional) True to keep existing selections
51073      */
51074     selectRow : function(index, keepExisting, preventViewNotify){
51075         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
51076         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
51077             if(!keepExisting || this.singleSelect){
51078                 this.clearSelections();
51079             }
51080             var r = this.grid.dataSource.getAt(index);
51081             this.selections.add(r);
51082             this.last = this.lastActive = index;
51083             if(!preventViewNotify){
51084                 this.grid.getView().onRowSelect(index);
51085             }
51086             this.fireEvent("rowselect", this, index, r);
51087             this.fireEvent("selectionchange", this);
51088         }
51089     },
51090
51091     /**
51092      * Deselects a row.
51093      * @param {Number} row The index of the row to deselect
51094      */
51095     deselectRow : function(index, preventViewNotify){
51096         if(this.locked) return;
51097         if(this.last == index){
51098             this.last = false;
51099         }
51100         if(this.lastActive == index){
51101             this.lastActive = false;
51102         }
51103         var r = this.grid.dataSource.getAt(index);
51104         this.selections.remove(r);
51105         if(!preventViewNotify){
51106             this.grid.getView().onRowDeselect(index);
51107         }
51108         this.fireEvent("rowdeselect", this, index);
51109         this.fireEvent("selectionchange", this);
51110     },
51111
51112     // private
51113     restoreLast : function(){
51114         if(this._last){
51115             this.last = this._last;
51116         }
51117     },
51118
51119     // private
51120     acceptsNav : function(row, col, cm){
51121         return !cm.isHidden(col) && cm.isCellEditable(col, row);
51122     },
51123
51124     // private
51125     onEditorKey : function(field, e){
51126         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
51127         if(k == e.TAB){
51128             e.stopEvent();
51129             ed.completeEdit();
51130             if(e.shiftKey){
51131                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
51132             }else{
51133                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
51134             }
51135         }else if(k == e.ENTER && !e.ctrlKey){
51136             e.stopEvent();
51137             ed.completeEdit();
51138             if(e.shiftKey){
51139                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
51140             }else{
51141                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
51142             }
51143         }else if(k == e.ESC){
51144             ed.cancelEdit();
51145         }
51146         if(newCell){
51147             g.startEditing(newCell[0], newCell[1]);
51148         }
51149     }
51150 });/*
51151  * Based on:
51152  * Ext JS Library 1.1.1
51153  * Copyright(c) 2006-2007, Ext JS, LLC.
51154  *
51155  * Originally Released Under LGPL - original licence link has changed is not relivant.
51156  *
51157  * Fork - LGPL
51158  * <script type="text/javascript">
51159  */
51160 /**
51161  * @class Roo.grid.CellSelectionModel
51162  * @extends Roo.grid.AbstractSelectionModel
51163  * This class provides the basic implementation for cell selection in a grid.
51164  * @constructor
51165  * @param {Object} config The object containing the configuration of this model.
51166  */
51167 Roo.grid.CellSelectionModel = function(config){
51168     Roo.apply(this, config);
51169
51170     this.selection = null;
51171
51172     this.addEvents({
51173         /**
51174              * @event beforerowselect
51175              * Fires before a cell is selected.
51176              * @param {SelectionModel} this
51177              * @param {Number} rowIndex The selected row index
51178              * @param {Number} colIndex The selected cell index
51179              */
51180             "beforecellselect" : true,
51181         /**
51182              * @event cellselect
51183              * Fires when a cell is selected.
51184              * @param {SelectionModel} this
51185              * @param {Number} rowIndex The selected row index
51186              * @param {Number} colIndex The selected cell index
51187              */
51188             "cellselect" : true,
51189         /**
51190              * @event selectionchange
51191              * Fires when the active selection changes.
51192              * @param {SelectionModel} this
51193              * @param {Object} selection null for no selection or an object (o) with two properties
51194                 <ul>
51195                 <li>o.record: the record object for the row the selection is in</li>
51196                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
51197                 </ul>
51198              */
51199             "selectionchange" : true
51200     });
51201     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
51202 };
51203
51204 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
51205
51206     /** @ignore */
51207     initEvents : function(){
51208         this.grid.on("mousedown", this.handleMouseDown, this);
51209         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
51210         var view = this.grid.view;
51211         view.on("refresh", this.onViewChange, this);
51212         view.on("rowupdated", this.onRowUpdated, this);
51213         view.on("beforerowremoved", this.clearSelections, this);
51214         view.on("beforerowsinserted", this.clearSelections, this);
51215         if(this.grid.isEditor){
51216             this.grid.on("beforeedit", this.beforeEdit,  this);
51217         }
51218     },
51219
51220         //private
51221     beforeEdit : function(e){
51222         this.select(e.row, e.column, false, true, e.record);
51223     },
51224
51225         //private
51226     onRowUpdated : function(v, index, r){
51227         if(this.selection && this.selection.record == r){
51228             v.onCellSelect(index, this.selection.cell[1]);
51229         }
51230     },
51231
51232         //private
51233     onViewChange : function(){
51234         this.clearSelections(true);
51235     },
51236
51237         /**
51238          * Returns the currently selected cell,.
51239          * @return {Array} The selected cell (row, column) or null if none selected.
51240          */
51241     getSelectedCell : function(){
51242         return this.selection ? this.selection.cell : null;
51243     },
51244
51245     /**
51246      * Clears all selections.
51247      * @param {Boolean} true to prevent the gridview from being notified about the change.
51248      */
51249     clearSelections : function(preventNotify){
51250         var s = this.selection;
51251         if(s){
51252             if(preventNotify !== true){
51253                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
51254             }
51255             this.selection = null;
51256             this.fireEvent("selectionchange", this, null);
51257         }
51258     },
51259
51260     /**
51261      * Returns true if there is a selection.
51262      * @return {Boolean}
51263      */
51264     hasSelection : function(){
51265         return this.selection ? true : false;
51266     },
51267
51268     /** @ignore */
51269     handleMouseDown : function(e, t){
51270         var v = this.grid.getView();
51271         if(this.isLocked()){
51272             return;
51273         };
51274         var row = v.findRowIndex(t);
51275         var cell = v.findCellIndex(t);
51276         if(row !== false && cell !== false){
51277             this.select(row, cell);
51278         }
51279     },
51280
51281     /**
51282      * Selects a cell.
51283      * @param {Number} rowIndex
51284      * @param {Number} collIndex
51285      */
51286     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
51287         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
51288             this.clearSelections();
51289             r = r || this.grid.dataSource.getAt(rowIndex);
51290             this.selection = {
51291                 record : r,
51292                 cell : [rowIndex, colIndex]
51293             };
51294             if(!preventViewNotify){
51295                 var v = this.grid.getView();
51296                 v.onCellSelect(rowIndex, colIndex);
51297                 if(preventFocus !== true){
51298                     v.focusCell(rowIndex, colIndex);
51299                 }
51300             }
51301             this.fireEvent("cellselect", this, rowIndex, colIndex);
51302             this.fireEvent("selectionchange", this, this.selection);
51303         }
51304     },
51305
51306         //private
51307     isSelectable : function(rowIndex, colIndex, cm){
51308         return !cm.isHidden(colIndex);
51309     },
51310
51311     /** @ignore */
51312     handleKeyDown : function(e){
51313         //Roo.log('Cell Sel Model handleKeyDown');
51314         if(!e.isNavKeyPress()){
51315             return;
51316         }
51317         var g = this.grid, s = this.selection;
51318         if(!s){
51319             e.stopEvent();
51320             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
51321             if(cell){
51322                 this.select(cell[0], cell[1]);
51323             }
51324             return;
51325         }
51326         var sm = this;
51327         var walk = function(row, col, step){
51328             return g.walkCells(row, col, step, sm.isSelectable,  sm);
51329         };
51330         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
51331         var newCell;
51332
51333         switch(k){
51334             case e.TAB:
51335                 // handled by onEditorKey
51336                 if (g.isEditor && g.editing) {
51337                     return;
51338                 }
51339                 if(e.shiftKey){
51340                      newCell = walk(r, c-1, -1);
51341                 }else{
51342                      newCell = walk(r, c+1, 1);
51343                 }
51344              break;
51345              case e.DOWN:
51346                  newCell = walk(r+1, c, 1);
51347              break;
51348              case e.UP:
51349                  newCell = walk(r-1, c, -1);
51350              break;
51351              case e.RIGHT:
51352                  newCell = walk(r, c+1, 1);
51353              break;
51354              case e.LEFT:
51355                  newCell = walk(r, c-1, -1);
51356              break;
51357              case e.ENTER:
51358                  if(g.isEditor && !g.editing){
51359                     g.startEditing(r, c);
51360                     e.stopEvent();
51361                     return;
51362                 }
51363              break;
51364         };
51365         if(newCell){
51366             this.select(newCell[0], newCell[1]);
51367             e.stopEvent();
51368         }
51369     },
51370
51371     acceptsNav : function(row, col, cm){
51372         return !cm.isHidden(col) && cm.isCellEditable(col, row);
51373     },
51374     /**
51375      * Selects a cell.
51376      * @param {Number} field (not used) - as it's normally used as a listener
51377      * @param {Number} e - event - fake it by using
51378      *
51379      * var e = Roo.EventObjectImpl.prototype;
51380      * e.keyCode = e.TAB
51381      *
51382      * 
51383      */
51384     onEditorKey : function(field, e){
51385         
51386         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
51387         ///Roo.log('onEditorKey' + k);
51388         if (!ed) {
51389             
51390             
51391             
51392         }
51393         if(k == e.TAB){
51394             if(e.shiftKey){
51395                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
51396             }else{
51397                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
51398             }
51399             
51400             e.stopEvent();
51401             
51402         }else if(k == e.ENTER &&  !e.ctrlKey){
51403             ed.completeEdit();
51404             e.stopEvent();
51405             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
51406         }else if(k == e.ESC){
51407             ed.cancelEdit();
51408         }
51409         
51410         
51411         if(newCell){
51412             //Roo.log('next cell after edit');
51413             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
51414         }
51415     }
51416 });/*
51417  * Based on:
51418  * Ext JS Library 1.1.1
51419  * Copyright(c) 2006-2007, Ext JS, LLC.
51420  *
51421  * Originally Released Under LGPL - original licence link has changed is not relivant.
51422  *
51423  * Fork - LGPL
51424  * <script type="text/javascript">
51425  */
51426  
51427 /**
51428  * @class Roo.grid.EditorGrid
51429  * @extends Roo.grid.Grid
51430  * Class for creating and editable grid.
51431  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
51432  * The container MUST have some type of size defined for the grid to fill. The container will be 
51433  * automatically set to position relative if it isn't already.
51434  * @param {Object} dataSource The data model to bind to
51435  * @param {Object} colModel The column model with info about this grid's columns
51436  */
51437 Roo.grid.EditorGrid = function(container, config){
51438     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
51439     this.getGridEl().addClass("xedit-grid");
51440
51441     if(!this.selModel){
51442         this.selModel = new Roo.grid.CellSelectionModel();
51443     }
51444
51445     this.activeEditor = null;
51446
51447         this.addEvents({
51448             /**
51449              * @event beforeedit
51450              * Fires before cell editing is triggered. The edit event object has the following properties <br />
51451              * <ul style="padding:5px;padding-left:16px;">
51452              * <li>grid - This grid</li>
51453              * <li>record - The record being edited</li>
51454              * <li>field - The field name being edited</li>
51455              * <li>value - The value for the field being edited.</li>
51456              * <li>row - The grid row index</li>
51457              * <li>column - The grid column index</li>
51458              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
51459              * </ul>
51460              * @param {Object} e An edit event (see above for description)
51461              */
51462             "beforeedit" : true,
51463             /**
51464              * @event afteredit
51465              * Fires after a cell is edited. <br />
51466              * <ul style="padding:5px;padding-left:16px;">
51467              * <li>grid - This grid</li>
51468              * <li>record - The record being edited</li>
51469              * <li>field - The field name being edited</li>
51470              * <li>value - The value being set</li>
51471              * <li>originalValue - The original value for the field, before the edit.</li>
51472              * <li>row - The grid row index</li>
51473              * <li>column - The grid column index</li>
51474              * </ul>
51475              * @param {Object} e An edit event (see above for description)
51476              */
51477             "afteredit" : true,
51478             /**
51479              * @event validateedit
51480              * Fires after a cell is edited, but before the value is set in the record. 
51481          * You can use this to modify the value being set in the field, Return false
51482              * to cancel the change. The edit event object has the following properties <br />
51483              * <ul style="padding:5px;padding-left:16px;">
51484          * <li>editor - This editor</li>
51485              * <li>grid - This grid</li>
51486              * <li>record - The record being edited</li>
51487              * <li>field - The field name being edited</li>
51488              * <li>value - The value being set</li>
51489              * <li>originalValue - The original value for the field, before the edit.</li>
51490              * <li>row - The grid row index</li>
51491              * <li>column - The grid column index</li>
51492              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
51493              * </ul>
51494              * @param {Object} e An edit event (see above for description)
51495              */
51496             "validateedit" : true
51497         });
51498     this.on("bodyscroll", this.stopEditing,  this);
51499     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
51500 };
51501
51502 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
51503     /**
51504      * @cfg {Number} clicksToEdit
51505      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
51506      */
51507     clicksToEdit: 2,
51508
51509     // private
51510     isEditor : true,
51511     // private
51512     trackMouseOver: false, // causes very odd FF errors
51513
51514     onCellDblClick : function(g, row, col){
51515         this.startEditing(row, col);
51516     },
51517
51518     onEditComplete : function(ed, value, startValue){
51519         this.editing = false;
51520         this.activeEditor = null;
51521         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
51522         var r = ed.record;
51523         var field = this.colModel.getDataIndex(ed.col);
51524         var e = {
51525             grid: this,
51526             record: r,
51527             field: field,
51528             originalValue: startValue,
51529             value: value,
51530             row: ed.row,
51531             column: ed.col,
51532             cancel:false,
51533             editor: ed
51534         };
51535         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
51536         cell.show();
51537           
51538         if(String(value) !== String(startValue)){
51539             
51540             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
51541                 r.set(field, e.value);
51542                 // if we are dealing with a combo box..
51543                 // then we also set the 'name' colum to be the displayField
51544                 if (ed.field.displayField && ed.field.name) {
51545                     r.set(ed.field.name, ed.field.el.dom.value);
51546                 }
51547                 
51548                 delete e.cancel; //?? why!!!
51549                 this.fireEvent("afteredit", e);
51550             }
51551         } else {
51552             this.fireEvent("afteredit", e); // always fire it!
51553         }
51554         this.view.focusCell(ed.row, ed.col);
51555     },
51556
51557     /**
51558      * Starts editing the specified for the specified row/column
51559      * @param {Number} rowIndex
51560      * @param {Number} colIndex
51561      */
51562     startEditing : function(row, col){
51563         this.stopEditing();
51564         if(this.colModel.isCellEditable(col, row)){
51565             this.view.ensureVisible(row, col, true);
51566           
51567             var r = this.dataSource.getAt(row);
51568             var field = this.colModel.getDataIndex(col);
51569             var cell = Roo.get(this.view.getCell(row,col));
51570             var e = {
51571                 grid: this,
51572                 record: r,
51573                 field: field,
51574                 value: r.data[field],
51575                 row: row,
51576                 column: col,
51577                 cancel:false 
51578             };
51579             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
51580                 this.editing = true;
51581                 var ed = this.colModel.getCellEditor(col, row);
51582                 
51583                 if (!ed) {
51584                     return;
51585                 }
51586                 if(!ed.rendered){
51587                     ed.render(ed.parentEl || document.body);
51588                 }
51589                 ed.field.reset();
51590                
51591                 cell.hide();
51592                 
51593                 (function(){ // complex but required for focus issues in safari, ie and opera
51594                     ed.row = row;
51595                     ed.col = col;
51596                     ed.record = r;
51597                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
51598                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
51599                     this.activeEditor = ed;
51600                     var v = r.data[field];
51601                     ed.startEdit(this.view.getCell(row, col), v);
51602                     // combo's with 'displayField and name set
51603                     if (ed.field.displayField && ed.field.name) {
51604                         ed.field.el.dom.value = r.data[ed.field.name];
51605                     }
51606                     
51607                     
51608                 }).defer(50, this);
51609             }
51610         }
51611     },
51612         
51613     /**
51614      * Stops any active editing
51615      */
51616     stopEditing : function(){
51617         if(this.activeEditor){
51618             this.activeEditor.completeEdit();
51619         }
51620         this.activeEditor = null;
51621     }
51622 });/*
51623  * Based on:
51624  * Ext JS Library 1.1.1
51625  * Copyright(c) 2006-2007, Ext JS, LLC.
51626  *
51627  * Originally Released Under LGPL - original licence link has changed is not relivant.
51628  *
51629  * Fork - LGPL
51630  * <script type="text/javascript">
51631  */
51632
51633 // private - not really -- you end up using it !
51634 // This is a support class used internally by the Grid components
51635
51636 /**
51637  * @class Roo.grid.GridEditor
51638  * @extends Roo.Editor
51639  * Class for creating and editable grid elements.
51640  * @param {Object} config any settings (must include field)
51641  */
51642 Roo.grid.GridEditor = function(field, config){
51643     if (!config && field.field) {
51644         config = field;
51645         field = Roo.factory(config.field, Roo.form);
51646     }
51647     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
51648     field.monitorTab = false;
51649 };
51650
51651 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
51652     
51653     /**
51654      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
51655      */
51656     
51657     alignment: "tl-tl",
51658     autoSize: "width",
51659     hideEl : false,
51660     cls: "x-small-editor x-grid-editor",
51661     shim:false,
51662     shadow:"frame"
51663 });/*
51664  * Based on:
51665  * Ext JS Library 1.1.1
51666  * Copyright(c) 2006-2007, Ext JS, LLC.
51667  *
51668  * Originally Released Under LGPL - original licence link has changed is not relivant.
51669  *
51670  * Fork - LGPL
51671  * <script type="text/javascript">
51672  */
51673   
51674
51675   
51676 Roo.grid.PropertyRecord = Roo.data.Record.create([
51677     {name:'name',type:'string'},  'value'
51678 ]);
51679
51680
51681 Roo.grid.PropertyStore = function(grid, source){
51682     this.grid = grid;
51683     this.store = new Roo.data.Store({
51684         recordType : Roo.grid.PropertyRecord
51685     });
51686     this.store.on('update', this.onUpdate,  this);
51687     if(source){
51688         this.setSource(source);
51689     }
51690     Roo.grid.PropertyStore.superclass.constructor.call(this);
51691 };
51692
51693
51694
51695 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
51696     setSource : function(o){
51697         this.source = o;
51698         this.store.removeAll();
51699         var data = [];
51700         for(var k in o){
51701             if(this.isEditableValue(o[k])){
51702                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
51703             }
51704         }
51705         this.store.loadRecords({records: data}, {}, true);
51706     },
51707
51708     onUpdate : function(ds, record, type){
51709         if(type == Roo.data.Record.EDIT){
51710             var v = record.data['value'];
51711             var oldValue = record.modified['value'];
51712             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
51713                 this.source[record.id] = v;
51714                 record.commit();
51715                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
51716             }else{
51717                 record.reject();
51718             }
51719         }
51720     },
51721
51722     getProperty : function(row){
51723        return this.store.getAt(row);
51724     },
51725
51726     isEditableValue: function(val){
51727         if(val && val instanceof Date){
51728             return true;
51729         }else if(typeof val == 'object' || typeof val == 'function'){
51730             return false;
51731         }
51732         return true;
51733     },
51734
51735     setValue : function(prop, value){
51736         this.source[prop] = value;
51737         this.store.getById(prop).set('value', value);
51738     },
51739
51740     getSource : function(){
51741         return this.source;
51742     }
51743 });
51744
51745 Roo.grid.PropertyColumnModel = function(grid, store){
51746     this.grid = grid;
51747     var g = Roo.grid;
51748     g.PropertyColumnModel.superclass.constructor.call(this, [
51749         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
51750         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
51751     ]);
51752     this.store = store;
51753     this.bselect = Roo.DomHelper.append(document.body, {
51754         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
51755             {tag: 'option', value: 'true', html: 'true'},
51756             {tag: 'option', value: 'false', html: 'false'}
51757         ]
51758     });
51759     Roo.id(this.bselect);
51760     var f = Roo.form;
51761     this.editors = {
51762         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
51763         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
51764         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
51765         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
51766         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
51767     };
51768     this.renderCellDelegate = this.renderCell.createDelegate(this);
51769     this.renderPropDelegate = this.renderProp.createDelegate(this);
51770 };
51771
51772 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
51773     
51774     
51775     nameText : 'Name',
51776     valueText : 'Value',
51777     
51778     dateFormat : 'm/j/Y',
51779     
51780     
51781     renderDate : function(dateVal){
51782         return dateVal.dateFormat(this.dateFormat);
51783     },
51784
51785     renderBool : function(bVal){
51786         return bVal ? 'true' : 'false';
51787     },
51788
51789     isCellEditable : function(colIndex, rowIndex){
51790         return colIndex == 1;
51791     },
51792
51793     getRenderer : function(col){
51794         return col == 1 ?
51795             this.renderCellDelegate : this.renderPropDelegate;
51796     },
51797
51798     renderProp : function(v){
51799         return this.getPropertyName(v);
51800     },
51801
51802     renderCell : function(val){
51803         var rv = val;
51804         if(val instanceof Date){
51805             rv = this.renderDate(val);
51806         }else if(typeof val == 'boolean'){
51807             rv = this.renderBool(val);
51808         }
51809         return Roo.util.Format.htmlEncode(rv);
51810     },
51811
51812     getPropertyName : function(name){
51813         var pn = this.grid.propertyNames;
51814         return pn && pn[name] ? pn[name] : name;
51815     },
51816
51817     getCellEditor : function(colIndex, rowIndex){
51818         var p = this.store.getProperty(rowIndex);
51819         var n = p.data['name'], val = p.data['value'];
51820         
51821         if(typeof(this.grid.customEditors[n]) == 'string'){
51822             return this.editors[this.grid.customEditors[n]];
51823         }
51824         if(typeof(this.grid.customEditors[n]) != 'undefined'){
51825             return this.grid.customEditors[n];
51826         }
51827         if(val instanceof Date){
51828             return this.editors['date'];
51829         }else if(typeof val == 'number'){
51830             return this.editors['number'];
51831         }else if(typeof val == 'boolean'){
51832             return this.editors['boolean'];
51833         }else{
51834             return this.editors['string'];
51835         }
51836     }
51837 });
51838
51839 /**
51840  * @class Roo.grid.PropertyGrid
51841  * @extends Roo.grid.EditorGrid
51842  * This class represents the  interface of a component based property grid control.
51843  * <br><br>Usage:<pre><code>
51844  var grid = new Roo.grid.PropertyGrid("my-container-id", {
51845       
51846  });
51847  // set any options
51848  grid.render();
51849  * </code></pre>
51850   
51851  * @constructor
51852  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
51853  * The container MUST have some type of size defined for the grid to fill. The container will be
51854  * automatically set to position relative if it isn't already.
51855  * @param {Object} config A config object that sets properties on this grid.
51856  */
51857 Roo.grid.PropertyGrid = function(container, config){
51858     config = config || {};
51859     var store = new Roo.grid.PropertyStore(this);
51860     this.store = store;
51861     var cm = new Roo.grid.PropertyColumnModel(this, store);
51862     store.store.sort('name', 'ASC');
51863     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
51864         ds: store.store,
51865         cm: cm,
51866         enableColLock:false,
51867         enableColumnMove:false,
51868         stripeRows:false,
51869         trackMouseOver: false,
51870         clicksToEdit:1
51871     }, config));
51872     this.getGridEl().addClass('x-props-grid');
51873     this.lastEditRow = null;
51874     this.on('columnresize', this.onColumnResize, this);
51875     this.addEvents({
51876          /**
51877              * @event beforepropertychange
51878              * Fires before a property changes (return false to stop?)
51879              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
51880              * @param {String} id Record Id
51881              * @param {String} newval New Value
51882          * @param {String} oldval Old Value
51883              */
51884         "beforepropertychange": true,
51885         /**
51886              * @event propertychange
51887              * Fires after a property changes
51888              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
51889              * @param {String} id Record Id
51890              * @param {String} newval New Value
51891          * @param {String} oldval Old Value
51892              */
51893         "propertychange": true
51894     });
51895     this.customEditors = this.customEditors || {};
51896 };
51897 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
51898     
51899      /**
51900      * @cfg {Object} customEditors map of colnames=> custom editors.
51901      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
51902      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
51903      * false disables editing of the field.
51904          */
51905     
51906       /**
51907      * @cfg {Object} propertyNames map of property Names to their displayed value
51908          */
51909     
51910     render : function(){
51911         Roo.grid.PropertyGrid.superclass.render.call(this);
51912         this.autoSize.defer(100, this);
51913     },
51914
51915     autoSize : function(){
51916         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
51917         if(this.view){
51918             this.view.fitColumns();
51919         }
51920     },
51921
51922     onColumnResize : function(){
51923         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
51924         this.autoSize();
51925     },
51926     /**
51927      * Sets the data for the Grid
51928      * accepts a Key => Value object of all the elements avaiable.
51929      * @param {Object} data  to appear in grid.
51930      */
51931     setSource : function(source){
51932         this.store.setSource(source);
51933         //this.autoSize();
51934     },
51935     /**
51936      * Gets all the data from the grid.
51937      * @return {Object} data  data stored in grid
51938      */
51939     getSource : function(){
51940         return this.store.getSource();
51941     }
51942 });/*
51943  * Based on:
51944  * Ext JS Library 1.1.1
51945  * Copyright(c) 2006-2007, Ext JS, LLC.
51946  *
51947  * Originally Released Under LGPL - original licence link has changed is not relivant.
51948  *
51949  * Fork - LGPL
51950  * <script type="text/javascript">
51951  */
51952  
51953 /**
51954  * @class Roo.LoadMask
51955  * A simple utility class for generically masking elements while loading data.  If the element being masked has
51956  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
51957  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
51958  * element's UpdateManager load indicator and will be destroyed after the initial load.
51959  * @constructor
51960  * Create a new LoadMask
51961  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
51962  * @param {Object} config The config object
51963  */
51964 Roo.LoadMask = function(el, config){
51965     this.el = Roo.get(el);
51966     Roo.apply(this, config);
51967     if(this.store){
51968         this.store.on('beforeload', this.onBeforeLoad, this);
51969         this.store.on('load', this.onLoad, this);
51970         this.store.on('loadexception', this.onLoadException, this);
51971         this.removeMask = false;
51972     }else{
51973         var um = this.el.getUpdateManager();
51974         um.showLoadIndicator = false; // disable the default indicator
51975         um.on('beforeupdate', this.onBeforeLoad, this);
51976         um.on('update', this.onLoad, this);
51977         um.on('failure', this.onLoad, this);
51978         this.removeMask = true;
51979     }
51980 };
51981
51982 Roo.LoadMask.prototype = {
51983     /**
51984      * @cfg {Boolean} removeMask
51985      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
51986      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
51987      */
51988     /**
51989      * @cfg {String} msg
51990      * The text to display in a centered loading message box (defaults to 'Loading...')
51991      */
51992     msg : 'Loading...',
51993     /**
51994      * @cfg {String} msgCls
51995      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
51996      */
51997     msgCls : 'x-mask-loading',
51998
51999     /**
52000      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
52001      * @type Boolean
52002      */
52003     disabled: false,
52004
52005     /**
52006      * Disables the mask to prevent it from being displayed
52007      */
52008     disable : function(){
52009        this.disabled = true;
52010     },
52011
52012     /**
52013      * Enables the mask so that it can be displayed
52014      */
52015     enable : function(){
52016         this.disabled = false;
52017     },
52018     
52019     onLoadException : function()
52020     {
52021         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
52022             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
52023         }
52024         this.el.unmask(this.removeMask);
52025     },
52026     // private
52027     onLoad : function()
52028     {
52029         this.el.unmask(this.removeMask);
52030     },
52031
52032     // private
52033     onBeforeLoad : function(){
52034         if(!this.disabled){
52035             this.el.mask(this.msg, this.msgCls);
52036         }
52037     },
52038
52039     // private
52040     destroy : function(){
52041         if(this.store){
52042             this.store.un('beforeload', this.onBeforeLoad, this);
52043             this.store.un('load', this.onLoad, this);
52044             this.store.un('loadexception', this.onLoadException, this);
52045         }else{
52046             var um = this.el.getUpdateManager();
52047             um.un('beforeupdate', this.onBeforeLoad, this);
52048             um.un('update', this.onLoad, this);
52049             um.un('failure', this.onLoad, this);
52050         }
52051     }
52052 };/*
52053  * Based on:
52054  * Ext JS Library 1.1.1
52055  * Copyright(c) 2006-2007, Ext JS, LLC.
52056  *
52057  * Originally Released Under LGPL - original licence link has changed is not relivant.
52058  *
52059  * Fork - LGPL
52060  * <script type="text/javascript">
52061  */
52062 Roo.XTemplate = function(){
52063     Roo.XTemplate.superclass.constructor.apply(this, arguments);
52064     var s = this.html;
52065
52066     s = ['<tpl>', s, '</tpl>'].join('');
52067
52068     var re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/;
52069
52070     var nameRe = /^<tpl\b[^>]*?for="(.*?)"/;
52071     var ifRe = /^<tpl\b[^>]*?if="(.*?)"/;
52072     var execRe = /^<tpl\b[^>]*?exec="(.*?)"/;
52073     var m, id = 0;
52074     var tpls = [];
52075
52076     while(m = s.match(re)){
52077        var m2 = m[0].match(nameRe);
52078        var m3 = m[0].match(ifRe);
52079        var m4 = m[0].match(execRe);
52080        var exp = null, fn = null, exec = null;
52081        var name = m2 && m2[1] ? m2[1] : '';
52082        if(m3){
52083            exp = m3 && m3[1] ? m3[1] : null;
52084            if(exp){
52085                fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
52086            }
52087        }
52088        if(m4){
52089            exp = m4 && m4[1] ? m4[1] : null;
52090            if(exp){
52091                exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
52092            }
52093        }
52094        if(name){
52095            switch(name){
52096                case '.': name = new Function('values', 'parent', 'with(values){ return values; }'); break;
52097                case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
52098                default: name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
52099            }
52100        }
52101        tpls.push({
52102             id: id,
52103             target: name,
52104             exec: exec,
52105             test: fn,
52106             body: m[1]||''
52107         });
52108        s = s.replace(m[0], '{xtpl'+ id + '}');
52109        ++id;
52110     }
52111     for(var i = tpls.length-1; i >= 0; --i){
52112         this.compileTpl(tpls[i]);
52113     }
52114     this.master = tpls[tpls.length-1];
52115     this.tpls = tpls;
52116 };
52117 Roo.extend(Roo.XTemplate, Roo.Template, {
52118
52119     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
52120
52121     applySubTemplate : function(id, values, parent){
52122         var t = this.tpls[id];
52123         if(t.test && !t.test.call(this, values, parent)){
52124             return '';
52125         }
52126         if(t.exec && t.exec.call(this, values, parent)){
52127             return '';
52128         }
52129         var vs = t.target ? t.target.call(this, values, parent) : values;
52130         parent = t.target ? values : parent;
52131         if(t.target && vs instanceof Array){
52132             var buf = [];
52133             for(var i = 0, len = vs.length; i < len; i++){
52134                 buf[buf.length] = t.compiled.call(this, vs[i], parent);
52135             }
52136             return buf.join('');
52137         }
52138         return t.compiled.call(this, vs, parent);
52139     },
52140
52141     compileTpl : function(tpl){
52142         var fm = Roo.util.Format;
52143         var useF = this.disableFormats !== true;
52144         var sep = Roo.isGecko ? "+" : ",";
52145         var fn = function(m, name, format, args){
52146             if(name.substr(0, 4) == 'xtpl'){
52147                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
52148             }
52149             var v;
52150             if(name.indexOf('.') != -1){
52151                 v = name;
52152             }else{
52153                 v = "values['" + name + "']";
52154             }
52155             if(format && useF){
52156                 args = args ? ',' + args : "";
52157                 if(format.substr(0, 5) != "this."){
52158                     format = "fm." + format + '(';
52159                 }else{
52160                     format = 'this.call("'+ format.substr(5) + '", ';
52161                     args = ", values";
52162                 }
52163             }else{
52164                 args= ''; format = "("+v+" === undefined ? '' : ";
52165             }
52166             return "'"+ sep + format + v + args + ")"+sep+"'";
52167         };
52168         var body;
52169         // branched to use + in gecko and [].join() in others
52170         if(Roo.isGecko){
52171             body = "tpl.compiled = function(values, parent){ return '" +
52172                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
52173                     "';};";
52174         }else{
52175             body = ["tpl.compiled = function(values, parent){ return ['"];
52176             body.push(tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
52177             body.push("'].join('');};");
52178             body = body.join('');
52179         }
52180         /** eval:var:zzzzzzz */
52181         eval(body);
52182         return this;
52183     },
52184
52185     applyTemplate : function(values){
52186         return this.master.compiled.call(this, values, {});
52187         var s = this.subs;
52188     },
52189
52190     apply : function(){
52191         return this.applyTemplate.apply(this, arguments);
52192     },
52193
52194     compile : function(){return this;}
52195 });
52196
52197 Roo.XTemplate.from = function(el){
52198     el = Roo.getDom(el);
52199     return new Roo.XTemplate(el.value || el.innerHTML);
52200 };/*
52201  * Original code for Roojs - LGPL
52202  * <script type="text/javascript">
52203  */
52204  
52205 /**
52206  * @class Roo.XComponent
52207  * A delayed Element creator...
52208  * Or a way to group chunks of interface together.
52209  * 
52210  * Mypart.xyx = new Roo.XComponent({
52211
52212     parent : 'Mypart.xyz', // empty == document.element.!!
52213     order : '001',
52214     name : 'xxxx'
52215     region : 'xxxx'
52216     disabled : function() {} 
52217      
52218     tree : function() { // return an tree of xtype declared components
52219         var MODULE = this;
52220         return 
52221         {
52222             xtype : 'NestedLayoutPanel',
52223             // technicall
52224         }
52225      ]
52226  *})
52227  *
52228  *
52229  * It can be used to build a big heiracy, with parent etc.
52230  * or you can just use this to render a single compoent to a dom element
52231  * MYPART.render(Roo.Element | String(id) | dom_element )
52232  * 
52233  * @extends Roo.util.Observable
52234  * @constructor
52235  * @param cfg {Object} configuration of component
52236  * 
52237  */
52238 Roo.XComponent = function(cfg) {
52239     Roo.apply(this, cfg);
52240     this.addEvents({ 
52241         /**
52242              * @event built
52243              * Fires when this the componnt is built
52244              * @param {Roo.XComponent} c the component
52245              */
52246         'built' : true,
52247         /**
52248              * @event buildcomplete
52249              * Fires on the top level element when all elements have been built
52250              * @param {Roo.XComponent} c the top level component.
52251          */
52252         'buildcomplete' : true
52253         
52254     });
52255     this.region = this.region || 'center'; // default..
52256     Roo.XComponent.register(this);
52257     this.modules = false;
52258     this.el = false; // where the layout goes..
52259     
52260     
52261 }
52262 Roo.extend(Roo.XComponent, Roo.util.Observable, {
52263     /**
52264      * @property el
52265      * The created element (with Roo.factory())
52266      * @type {Roo.Layout}
52267      */
52268     el  : false,
52269     
52270     /**
52271      * @property el
52272      * for BC  - use el in new code
52273      * @type {Roo.Layout}
52274      */
52275     panel : false,
52276     
52277     /**
52278      * @property layout
52279      * for BC  - use el in new code
52280      * @type {Roo.Layout}
52281      */
52282     layout : false,
52283     
52284      /**
52285      * @cfg {Function|boolean} disabled
52286      * If this module is disabled by some rule, return true from the funtion
52287      */
52288     disabled : false,
52289     
52290     /**
52291      * @cfg {String} parent 
52292      * Name of parent element which it get xtype added to..
52293      */
52294     parent: false,
52295     
52296     /**
52297      * @cfg {String} order
52298      * Used to set the order in which elements are created (usefull for multiple tabs)
52299      */
52300     
52301     order : false,
52302     /**
52303      * @cfg {String} name
52304      * String to display while loading.
52305      */
52306     name : false,
52307     /**
52308      * @cfg {String} region
52309      * Region to render component to (defaults to center)
52310      */
52311     region : 'center',
52312     
52313     /**
52314      * @cfg {Array} items
52315      * A single item array - the first element is the root of the tree..
52316      * It's done this way to stay compatible with the Xtype system...
52317      */
52318     items : false,
52319     
52320     
52321      /**
52322      * render
52323      * render element to dom or tree
52324      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
52325      */
52326     
52327     render : function(el)
52328     {
52329         
52330         el = el || false;
52331         var hp = this.parent ? 1 : 0;
52332         
52333         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
52334             // if parent is a '#.....' string, then let's use that..
52335             var ename = this.parent.substr(1)
52336             this.parent = false;
52337             el = Roo.get(ename);
52338             if (!el) {
52339                 Roo.log("Warning - element can not be found :#" + ename );
52340                 return;
52341             }
52342         }
52343         
52344         
52345         if (!this.parent) {
52346             
52347             el = el ? Roo.get(el) : false;
52348             
52349             // it's a top level one..
52350             this.parent =  {
52351                 el : new Roo.BorderLayout(el || document.body, {
52352                 
52353                      center: {
52354                          titlebar: false,
52355                          autoScroll:false,
52356                          closeOnTab: true,
52357                          tabPosition: 'top',
52358                           //resizeTabs: true,
52359                          alwaysShowTabs: el && hp? false :  true,
52360                          hideTabs: el || !hp ? true :  false,
52361                          minTabWidth: 140
52362                      }
52363                  })
52364             }
52365         }
52366         
52367         
52368             
52369         var tree = this.tree();
52370         tree.region = tree.region || this.region;
52371         this.el = this.parent.el.addxtype(tree);
52372         this.fireEvent('built', this);
52373         
52374         this.panel = this.el;
52375         this.layout = this.panel.layout;    
52376          
52377     }
52378     
52379 });
52380
52381 Roo.apply(Roo.XComponent, {
52382     
52383     /**
52384      * @property  buildCompleted
52385      * True when the builder has completed building the interface.
52386      * @type Boolean
52387      */
52388     buildCompleted : false,
52389      
52390     /**
52391      * @property  topModule
52392      * the upper most module - uses document.element as it's constructor.
52393      * @type Object
52394      */
52395      
52396     topModule  : false,
52397       
52398     /**
52399      * @property  modules
52400      * array of modules to be created by registration system.
52401      * @type {Array} of Roo.XComponent
52402      */
52403     
52404     modules : [],
52405     /**
52406      * @property  elmodules
52407      * array of modules to be created by which use #ID 
52408      * @type {Array} of Roo.XComponent
52409      */
52410      
52411     elmodules : [],
52412
52413     
52414     /**
52415      * Register components to be built later.
52416      *
52417      * This solves the following issues
52418      * - Building is not done on page load, but after an authentication process has occured.
52419      * - Interface elements are registered on page load
52420      * - Parent Interface elements may not be loaded before child, so this handles that..
52421      * 
52422      *
52423      * example:
52424      * 
52425      * MyApp.register({
52426           order : '000001',
52427           module : 'Pman.Tab.projectMgr',
52428           region : 'center',
52429           parent : 'Pman.layout',
52430           disabled : false,  // or use a function..
52431         })
52432      
52433      * * @param {Object} details about module
52434      */
52435     register : function(obj) {
52436         this.modules.push(obj);
52437          
52438     },
52439     /**
52440      * convert a string to an object..
52441      * eg. 'AAA.BBB' -> finds AAA.BBB
52442
52443      */
52444     
52445     toObject : function(str)
52446     {
52447         if (!str || typeof(str) == 'object') {
52448             return str;
52449         }
52450         if (str.substring(0,1) == '#') {
52451             return str;
52452         }
52453
52454         var ar = str.split('.');
52455         var rt, o;
52456         rt = ar.shift();
52457             /** eval:var:o */
52458         try {
52459             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
52460         } catch (e) {
52461             throw "Module not found : " + str;
52462         }
52463         
52464         if (o === false) {
52465             throw "Module not found : " + str;
52466         }
52467         Roo.each(ar, function(e) {
52468             if (typeof(o[e]) == 'undefined') {
52469                 throw "Module not found : " + str;
52470             }
52471             o = o[e];
52472         });
52473         
52474         return o;
52475         
52476     },
52477     
52478     
52479     /**
52480      * move modules into their correct place in the tree..
52481      * 
52482      */
52483     preBuild : function ()
52484     {
52485         var _t = this;
52486         Roo.each(this.modules , function (obj)
52487         {
52488             var opar = obj.parent;
52489             try { 
52490                 obj.parent = this.toObject(opar);
52491             } catch(e) {
52492                 Roo.log(e.toString());
52493                 return;
52494             }
52495             
52496             if (!obj.parent) {
52497                 this.topModule = obj;
52498                 return;
52499             }
52500             if (typeof(obj.parent) == 'string') {
52501                 this.elmodules.push(obj);
52502                 return;
52503             }
52504             if (obj.parent.constructor != Roo.XComponent) {
52505                 Roo.log("Object Parent is not instance of XComponent:" + obj.name)
52506             }
52507             if (!obj.parent.modules) {
52508                 obj.parent.modules = new Roo.util.MixedCollection(false, 
52509                     function(o) { return o.order + '' }
52510                 );
52511             }
52512             
52513             obj.parent.modules.add(obj);
52514         }, this);
52515     },
52516     
52517      /**
52518      * make a list of modules to build.
52519      * @return {Array} list of modules. 
52520      */ 
52521     
52522     buildOrder : function()
52523     {
52524         var _this = this;
52525         var cmp = function(a,b) {   
52526             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
52527         };
52528         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
52529             throw "No top level modules to build";
52530         }
52531         
52532         // make a flat list in order of modules to build.
52533         var mods = this.topModule ? [ this.topModule ] : [];
52534         Roo.each(this.elmodules,function(e) { mods.push(e) });
52535
52536         
52537         // add modules to their parents..
52538         var addMod = function(m) {
52539            // Roo.debug && Roo.log(m.modKey);
52540             
52541             mods.push(m);
52542             if (m.modules) {
52543                 m.modules.keySort('ASC',  cmp );
52544                 m.modules.each(addMod);
52545             }
52546             // not sure if this is used any more..
52547             if (m.finalize) {
52548                 m.finalize.name = m.name + " (clean up) ";
52549                 mods.push(m.finalize);
52550             }
52551             
52552         }
52553         if (this.topModule) { 
52554             this.topModule.modules.keySort('ASC',  cmp );
52555             this.topModule.modules.each(addMod);
52556         }
52557         return mods;
52558     },
52559     
52560      /**
52561      * Build the registered modules.
52562      * @param {Object} parent element.
52563      * @param {Function} optional method to call after module has been added.
52564      * 
52565      */ 
52566    
52567     build : function() 
52568     {
52569         
52570         this.preBuild();
52571         var mods = this.buildOrder();
52572       
52573         //this.allmods = mods;
52574         //Roo.debug && Roo.log(mods);
52575         //return;
52576         if (!mods.length) { // should not happen
52577             throw "NO modules!!!";
52578         }
52579         
52580         
52581         
52582         // flash it up as modal - so we store the mask!?
52583         Roo.MessageBox.show({ title: 'loading' });
52584         Roo.MessageBox.show({
52585            title: "Please wait...",
52586            msg: "Building Interface...",
52587            width:450,
52588            progress:true,
52589            closable:false,
52590            modal: false
52591           
52592         });
52593         var total = mods.length;
52594         
52595         var _this = this;
52596         var progressRun = function() {
52597             if (!mods.length) {
52598                 Roo.debug && Roo.log('hide?');
52599                 Roo.MessageBox.hide();
52600                 if (_this.topModule) { 
52601                     _this.topModule.fireEvent('buildcomplete', _this.topModule);
52602                 }
52603                 // THE END...
52604                 return false;   
52605             }
52606             
52607             var m = mods.shift();
52608             
52609             
52610             Roo.debug && Roo.log(m);
52611             // not sure if this is supported any more.. - modules that are are just function
52612             if (typeof(m) == 'function') { 
52613                 m.call(this);
52614                 return progressRun.defer(10, _this);
52615             } 
52616             
52617             
52618             
52619             Roo.MessageBox.updateProgress(
52620                 (total  - mods.length)/total,  "Building Interface " + (total  - mods.length) + 
52621                     " of " + total + 
52622                     (m.name ? (' - ' + m.name) : '')
52623                     );
52624             
52625          
52626             // is the module disabled?
52627             var disabled = (typeof(m.disabled) == 'function') ?
52628                 m.disabled.call(m.module.disabled) : m.disabled;    
52629             
52630             
52631             if (disabled) {
52632                 return progressRun(); // we do not update the display!
52633             }
52634             
52635             // now build 
52636             
52637             m.render();
52638             // it's 10 on top level, and 1 on others??? why...
52639             return progressRun.defer(10, _this);
52640              
52641         }
52642         progressRun.defer(1, _this);
52643      
52644         
52645         
52646     }
52647     
52648      
52649    
52650     
52651     
52652 });
52653  //<script type="text/javascript">
52654
52655
52656 /**
52657  * @class Roo.Login
52658  * @extends Roo.LayoutDialog
52659  * A generic Login Dialog..... - only one needed in theory!?!?
52660  *
52661  * Fires XComponent builder on success...
52662  * 
52663  * Sends 
52664  *    username,password, lang = for login actions.
52665  *    check = 1 for periodic checking that sesion is valid.
52666  *    passwordRequest = email request password
52667  *    logout = 1 = to logout
52668  * 
52669  * Affects: (this id="????" elements)
52670  *   loading  (removed) (used to indicate application is loading)
52671  *   loading-mask (hides) (used to hide application when it's building loading)
52672  *   
52673  * 
52674  * Usage: 
52675  *    
52676  * 
52677  * Myapp.login = Roo.Login({
52678      url: xxxx,
52679    
52680      realm : 'Myapp', 
52681      
52682      
52683      method : 'POST',
52684      
52685      
52686      * 
52687  })
52688  * 
52689  * 
52690  * 
52691  **/
52692  
52693 Roo.Login = function(cfg)
52694 {
52695     this.addEvents({
52696         'refreshed' : true
52697     });
52698     
52699     Roo.apply(this,cfg);
52700     
52701     Roo.onReady(function() {
52702         this.onLoad();
52703     }, this);
52704     // call parent..
52705     
52706    
52707     Roo.Login.superclass.constructor.call(this, this);
52708     //this.addxtype(this.items[0]);
52709     
52710     
52711 }
52712
52713
52714 Roo.extend(Roo.Login, Roo.LayoutDialog, {
52715     
52716     /**
52717      * @cfg {String} method
52718      * Method used to query for login details.
52719      */
52720     
52721     method : 'POST',
52722     /**
52723      * @cfg {String} url
52724      * URL to query login data. - eg. baseURL + '/Login.php'
52725      */
52726     url : '',
52727     
52728     /**
52729      * @property user
52730      * The user data - if user.id < 0 then login will be bypassed. (used for inital setup situation.
52731      * @type {Object} 
52732      */
52733     user : false,
52734     /**
52735      * @property checkFails
52736      * Number of times we have attempted to get authentication check, and failed.
52737      * @type {Number} 
52738      */
52739     checkFails : 0,
52740       /**
52741      * @property intervalID
52742      * The window interval that does the constant login checking.
52743      * @type {Number} 
52744      */
52745     intervalID : 0,
52746     
52747     
52748     onLoad : function() // called on page load...
52749     {
52750         // load 
52751          
52752         if (Roo.get('loading')) { // clear any loading indicator..
52753             Roo.get('loading').remove();
52754         }
52755         
52756         //this.switchLang('en'); // set the language to english..
52757        
52758         this.check({
52759             success:  function(response, opts)  {  // check successfull...
52760             
52761                 var res = this.processResponse(response);
52762                 this.checkFails =0;
52763                 if (!res.success) { // error!
52764                     this.checkFails = 5;
52765                     //console.log('call failure');
52766                     return this.failure(response,opts);
52767                 }
52768                 
52769                 if (!res.data.id) { // id=0 == login failure.
52770                     return this.show();
52771                 }
52772                 
52773                               
52774                         //console.log(success);
52775                 this.fillAuth(res.data);   
52776                 this.checkFails =0;
52777                 Roo.XComponent.build();
52778             },
52779             failure : this.show
52780         });
52781         
52782     }, 
52783     
52784     
52785     check: function(cfg) // called every so often to refresh cookie etc..
52786     {
52787         if (cfg.again) { // could be undefined..
52788             this.checkFails++;
52789         } else {
52790             this.checkFails = 0;
52791         }
52792         var _this = this;
52793         if (this.sending) {
52794             if ( this.checkFails > 4) {
52795                 Roo.MessageBox.alert("Error",  
52796                     "Error getting authentication status. - try reloading, or wait a while", function() {
52797                         _this.sending = false;
52798                     }); 
52799                 return;
52800             }
52801             cfg.again = true;
52802             _this.check.defer(10000, _this, [ cfg ]); // check in 10 secs.
52803             return;
52804         }
52805         this.sending = true;
52806         
52807         Roo.Ajax.request({  
52808             url: this.url,
52809             params: {
52810                 getAuthUser: true
52811             },  
52812             method: this.method,
52813             success:  cfg.success || this.success,
52814             failure : cfg.failure || this.failure,
52815             scope : this,
52816             callCfg : cfg
52817               
52818         });  
52819     }, 
52820     
52821     
52822     logout: function()
52823     {
52824         window.onbeforeunload = function() { }; // false does not work for IE..
52825         this.user = false;
52826         var _this = this;
52827         
52828         Roo.Ajax.request({  
52829             url: this.url,
52830             params: {
52831                 logout: 1
52832             },  
52833             method: 'GET',
52834             failure : function() {
52835                 Roo.MessageBox.alert("Error", "Error logging out. - continuing anyway.", function() {
52836                     document.location = document.location.toString() + '?ts=' + Math.random();
52837                 });
52838                 
52839             },
52840             success : function() {
52841                 _this.user = false;
52842                 this.checkFails =0;
52843                 // fixme..
52844                 document.location = document.location.toString() + '?ts=' + Math.random();
52845             }
52846               
52847               
52848         }); 
52849     },
52850     
52851     processResponse : function (response)
52852     {
52853         var res = '';
52854         try {
52855             res = Roo.decode(response.responseText);
52856             // oops...
52857             if (typeof(res) != 'object') {
52858                 res = { success : false, errorMsg : res, errors : true };
52859             }
52860             if (typeof(res.success) == 'undefined') {
52861                 res.success = false;
52862             }
52863             
52864         } catch(e) {
52865             res = { success : false,  errorMsg : response.responseText, errors : true };
52866         }
52867         return res;
52868     },
52869     
52870     success : function(response, opts)  // check successfull...
52871     {  
52872         this.sending = false;
52873         var res = this.processResponse(response);
52874         if (!res.success) {
52875             return this.failure(response, opts);
52876         }
52877         if (!res.data || !res.data.id) {
52878             return this.failure(response,opts);
52879         }
52880         //console.log(res);
52881         this.fillAuth(res.data);
52882         
52883         this.checkFails =0;
52884         
52885     },
52886     
52887     
52888     failure : function (response, opts) // called if login 'check' fails.. (causes re-check)
52889     {
52890         this.authUser = -1;
52891         this.sending = false;
52892         var res = this.processResponse(response);
52893         //console.log(res);
52894         if ( this.checkFails > 2) {
52895         
52896             Roo.MessageBox.alert("Error", res.errorMsg ? res.errorMsg : 
52897                 "Error getting authentication status. - try reloading"); 
52898             return;
52899         }
52900         opts.callCfg.again = true;
52901         this.check.defer(1000, this, [ opts.callCfg ]);
52902         return;  
52903     },
52904     
52905     
52906     
52907     fillAuth: function(au) {
52908         this.startAuthCheck();
52909         this.authUserId = au.id;
52910         this.authUser = au;
52911         this.lastChecked = new Date();
52912         this.fireEvent('refreshed', au);
52913         //Pman.Tab.FaxQueue.newMaxId(au.faxMax);
52914         //Pman.Tab.FaxTab.setTitle(au.faxNumPending);
52915         au.lang = au.lang || 'en';
52916         //this.switchLang(Roo.state.Manager.get('Pman.Login.lang', 'en'));
52917         Roo.state.Manager.set( this.realm + 'lang' , au.lang);
52918         this.switchLang(au.lang );
52919         
52920      
52921         // open system... - -on setyp..
52922         if (this.authUserId  < 0) {
52923             Roo.MessageBox.alert("Warning", 
52924                 "This is an open system - please set up a admin user with a password.");  
52925         }
52926          
52927         //Pman.onload(); // which should do nothing if it's a re-auth result...
52928         
52929              
52930     },
52931     
52932     startAuthCheck : function() // starter for timeout checking..
52933     {
52934         if (this.intervalID) { // timer already in place...
52935             return false;
52936         }
52937         var _this = this;
52938         this.intervalID =  window.setInterval(function() {
52939               _this.check(false);
52940             }, 120000); // every 120 secs = 2mins..
52941         
52942         
52943     },
52944          
52945     
52946     switchLang : function (lang) 
52947     {
52948         _T = typeof(_T) == 'undefined' ? false : _T;
52949           if (!_T || !lang.length) {
52950             return;
52951         }
52952         
52953         if (!_T && lang != 'en') {
52954             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
52955             return;
52956         }
52957         
52958         if (typeof(_T.en) == 'undefined') {
52959             _T.en = {};
52960             Roo.apply(_T.en, _T);
52961         }
52962         
52963         if (typeof(_T[lang]) == 'undefined') {
52964             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
52965             return;
52966         }
52967         
52968         
52969         Roo.apply(_T, _T[lang]);
52970         // just need to set the text values for everything...
52971         var _this = this;
52972         /* this will not work ...
52973         if (this.form) { 
52974             
52975                
52976             function formLabel(name, val) {
52977                 _this.form.findField(name).fieldEl.child('label').dom.innerHTML  = val;
52978             }
52979             
52980             formLabel('password', "Password"+':');
52981             formLabel('username', "Email Address"+':');
52982             formLabel('lang', "Language"+':');
52983             this.dialog.setTitle("Login");
52984             this.dialog.buttons[0].setText("Forgot Password");
52985             this.dialog.buttons[1].setText("Login");
52986         }
52987         */
52988         
52989         
52990     },
52991     
52992     
52993     title: "Login",
52994     modal: true,
52995     width:  350,
52996     //height: 230,
52997     height: 180,
52998     shadow: true,
52999     minWidth:200,
53000     minHeight:180,
53001     //proxyDrag: true,
53002     closable: false,
53003     draggable: false,
53004     collapsible: false,
53005     resizable: false,
53006     center: {  // needed??
53007         autoScroll:false,
53008         titlebar: false,
53009        // tabPosition: 'top',
53010         hideTabs: true,
53011         closeOnTab: true,
53012         alwaysShowTabs: false
53013     } ,
53014     listeners : {
53015         
53016         show  : function(dlg)
53017         {
53018             //console.log(this);
53019             this.form = this.layout.getRegion('center').activePanel.form;
53020             this.form.dialog = dlg;
53021             this.buttons[0].form = this.form;
53022             this.buttons[0].dialog = dlg;
53023             this.buttons[1].form = this.form;
53024             this.buttons[1].dialog = dlg;
53025            
53026            //this.resizeToLogo.defer(1000,this);
53027             // this is all related to resizing for logos..
53028             //var sz = Roo.get(Pman.Login.form.el.query('img')[0]).getSize();
53029            //// if (!sz) {
53030              //   this.resizeToLogo.defer(1000,this);
53031              //   return;
53032            // }
53033             //var w = Ext.lib.Dom.getViewWidth() - 100;
53034             //var h = Ext.lib.Dom.getViewHeight() - 100;
53035             //this.resizeTo(Math.max(350, Math.min(sz.width + 30, w)),Math.min(sz.height+200, h));
53036             //this.center();
53037             if (this.disabled) {
53038                 this.hide();
53039                 return;
53040             }
53041             
53042             if (this.user.id < 0) { // used for inital setup situations.
53043                 return;
53044             }
53045             
53046             if (this.intervalID) {
53047                 // remove the timer
53048                 window.clearInterval(this.intervalID);
53049                 this.intervalID = false;
53050             }
53051             
53052             
53053             if (Roo.get('loading')) {
53054                 Roo.get('loading').remove();
53055             }
53056             if (Roo.get('loading-mask')) {
53057                 Roo.get('loading-mask').hide();
53058             }
53059             
53060             //incomming._node = tnode;
53061             this.form.reset();
53062             //this.dialog.modal = !modal;
53063             //this.dialog.show();
53064             this.el.unmask(); 
53065             
53066             
53067             this.form.setValues({
53068                 'username' : Roo.state.Manager.get(this.realm + '.username', ''),
53069                 'lang' : Roo.state.Manager.get(this.realm + '.lang', 'en')
53070             });
53071             
53072             this.switchLang(Roo.state.Manager.get(this.realm + '.lang', 'en'));
53073             if (this.form.findField('username').getValue().length > 0 ){
53074                 this.form.findField('password').focus();
53075             } else {
53076                this.form.findField('username').focus();
53077             }
53078     
53079         }
53080     },
53081     items : [
53082          {
53083        
53084             xtype : 'ContentPanel',
53085             xns : Roo,
53086             region: 'center',
53087             fitToFrame : true,
53088             
53089             items : [
53090     
53091                 {
53092                
53093                     xtype : 'Form',
53094                     xns : Roo.form,
53095                     labelWidth: 100,
53096                     style : 'margin: 10px;',
53097                     
53098                     listeners : {
53099                         actionfailed : function(f, act) {
53100                             // form can return { errors: .... }
53101                                 
53102                             //act.result.errors // invalid form element list...
53103                             //act.result.errorMsg// invalid form element list...
53104                             
53105                             this.dialog.el.unmask();
53106                             Roo.MessageBox.alert("Error", act.result.errorMsg ? act.result.errorMsg : 
53107                                         "Login failed - communication error - try again.");
53108                                       
53109                         },
53110                         actioncomplete: function(re, act) {
53111                              
53112                             Roo.state.Manager.set(
53113                                 this.dialog.realm + '.username',  
53114                                     this.findField('username').getValue()
53115                             );
53116                             Roo.state.Manager.set(
53117                                 this.dialog.realm + '.lang',  
53118                                 this.findField('lang').getValue() 
53119                             );
53120                             
53121                             this.dialog.fillAuth(act.result.data);
53122                               
53123                             this.dialog.hide();
53124                             
53125                             if (Roo.get('loading-mask')) {
53126                                 Roo.get('loading-mask').show();
53127                             }
53128                             Roo.XComponent.build();
53129                             
53130                              
53131                             
53132                         }
53133                     },
53134                     items : [
53135                         {
53136                             xtype : 'TextField',
53137                             xns : Roo.form,
53138                             fieldLabel: "Email Address",
53139                             name: 'username',
53140                             width:200,
53141                             autoCreate : {tag: "input", type: "text", size: "20"}
53142                         },
53143                         {
53144                             xtype : 'TextField',
53145                             xns : Roo.form,
53146                             fieldLabel: "Password",
53147                             inputType: 'password',
53148                             name: 'password',
53149                             width:200,
53150                             autoCreate : {tag: "input", type: "text", size: "20"},
53151                             listeners : {
53152                                 specialkey : function(e,ev) {
53153                                     if (ev.keyCode == 13) {
53154                                         this.form.dialog.el.mask("Logging in");
53155                                         this.form.doAction('submit', {
53156                                             url: this.form.dialog.url,
53157                                             method: this.form.dialog.method
53158                                         });
53159                                     }
53160                                 }
53161                             }  
53162                         },
53163                         {
53164                             xtype : 'ComboBox',
53165                             xns : Roo.form,
53166                             fieldLabel: "Language",
53167                             name : 'langdisp',
53168                             store: {
53169                                 xtype : 'SimpleStore',
53170                                 fields: ['lang', 'ldisp'],
53171                                 data : [
53172                                     [ 'en', 'English' ],
53173                                     [ 'zh_HK' , '\u7E41\u4E2D' ],
53174                                     [ 'zh_CN', '\u7C21\u4E2D' ]
53175                                 ]
53176                             },
53177                             
53178                             valueField : 'lang',
53179                             hiddenName:  'lang',
53180                             width: 200,
53181                             displayField:'ldisp',
53182                             typeAhead: false,
53183                             editable: false,
53184                             mode: 'local',
53185                             triggerAction: 'all',
53186                             emptyText:'Select a Language...',
53187                             selectOnFocus:true,
53188                             listeners : {
53189                                 select :  function(cb, rec, ix) {
53190                                     this.form.switchLang(rec.data.lang);
53191                                 }
53192                             }
53193                         
53194                         }
53195                     ]
53196                 }
53197                   
53198                 
53199             ]
53200         }
53201     ],
53202     buttons : [
53203         {
53204             xtype : 'Button',
53205             xns : 'Roo',
53206             text : "Forgot Password",
53207             listeners : {
53208                 click : function() {
53209                     //console.log(this);
53210                     var n = this.form.findField('username').getValue();
53211                     if (!n.length) {
53212                         Roo.MessageBox.alert("Error", "Fill in your email address");
53213                         return;
53214                     }
53215                     Roo.Ajax.request({
53216                         url: this.dialog.url,
53217                         params: {
53218                             passwordRequest: n
53219                         },
53220                         method: this.dialog.method,
53221                         success:  function(response, opts)  {  // check successfull...
53222                         
53223                             var res = this.dialog.processResponse(response);
53224                             if (!res.success) { // error!
53225                                Roo.MessageBox.alert("Error" ,
53226                                     res.errorMsg ? res.errorMsg  : "Problem Requesting Password Reset");
53227                                return;
53228                             }
53229                             Roo.MessageBox.alert("Notice" ,
53230                                 "Please check you email for the Password Reset message");
53231                         },
53232                         failure : function() {
53233                             Roo.MessageBox.alert("Error" , "Problem Requesting Password Reset");
53234                         }
53235                         
53236                     });
53237                 }
53238             }
53239         },
53240         {
53241             xtype : 'Button',
53242             xns : 'Roo',
53243             text : "Login",
53244             listeners : {
53245                 
53246                 click : function () {
53247                         
53248                     this.dialog.el.mask("Logging in");
53249                     this.form.doAction('submit', {
53250                             url: this.dialog.url,
53251                             method: this.dialog.method
53252                     });
53253                 }
53254             }
53255         }
53256     ]
53257   
53258   
53259 })
53260  
53261
53262
53263