roojs-debug.js
[roojs1] / roojs-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12
13
14
15
16 // for old browsers
17 window["undefined"] = window["undefined"];
18
19 /**
20  * @class Roo
21  * Roo core utilities and functions.
22  * @singleton
23  */
24 var Roo = {}; 
25 /**
26  * Copies all the properties of config to obj.
27  * @param {Object} obj The receiver of the properties
28  * @param {Object} config The source of the properties
29  * @param {Object} defaults A different object that will also be applied for default values
30  * @return {Object} returns obj
31  * @member Roo apply
32  */
33
34  
35 Roo.apply = function(o, c, defaults){
36     if(defaults){
37         // no "this" reference for friendly out of scope calls
38         Roo.apply(o, defaults);
39     }
40     if(o && c && typeof c == 'object'){
41         for(var p in c){
42             o[p] = c[p];
43         }
44     }
45     return o;
46 };
47
48
49 (function(){
50     var idSeed = 0;
51     var ua = navigator.userAgent.toLowerCase();
52
53     var isStrict = document.compatMode == "CSS1Compat",
54         isOpera = ua.indexOf("opera") > -1,
55         isSafari = (/webkit|khtml/).test(ua),
56         isIE = ua.indexOf("msie") > -1,
57         isIE7 = ua.indexOf("msie 7") > -1,
58         isGecko = !isSafari && ua.indexOf("gecko") > -1,
59         isBorderBox = isIE && !isStrict,
60         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
61         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
62         isLinux = (ua.indexOf("linux") != -1),
63         isSecure = window.location.href.toLowerCase().indexOf("https") === 0;
64
65     // remove css image flicker
66         if(isIE && !isIE7){
67         try{
68             document.execCommand("BackgroundImageCache", false, true);
69         }catch(e){}
70     }
71
72     Roo.apply(Roo, {
73         /**
74          * True if the browser is in strict mode
75          * @type Boolean
76          */
77         isStrict : isStrict,
78         /**
79          * True if the page is running over SSL
80          * @type Boolean
81          */
82         isSecure : isSecure,
83         /**
84          * True when the document is fully initialized and ready for action
85          * @type Boolean
86          */
87         isReady : false,
88         /**
89          * Turn on debugging output (currently only the factory uses this)
90          * @type Boolean
91          */
92         
93         debug: false,
94
95         /**
96          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
97          * @type Boolean
98          */
99         enableGarbageCollector : true,
100
101         /**
102          * True to automatically purge event listeners after uncaching an element (defaults to false).
103          * Note: this only happens if enableGarbageCollector is true.
104          * @type Boolean
105          */
106         enableListenerCollection:false,
107
108         /**
109          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
110          * the IE insecure content warning (defaults to javascript:false).
111          * @type String
112          */
113         SSL_SECURE_URL : "javascript:false",
114
115         /**
116          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
117          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
118          * @type String
119          */
120         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
121
122         emptyFn : function(){},
123
124         /**
125          * Copies all the properties of config to obj if they don't already exist.
126          * @param {Object} obj The receiver of the properties
127          * @param {Object} config The source of the properties
128          * @return {Object} returns obj
129          */
130         applyIf : function(o, c){
131             if(o && c){
132                 for(var p in c){
133                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
134                 }
135             }
136             return o;
137         },
138
139         /**
140          * Applies event listeners to elements by selectors when the document is ready.
141          * The event name is specified with an @ suffix.
142 <pre><code>
143 Roo.addBehaviors({
144    // add a listener for click on all anchors in element with id foo
145    '#foo a@click' : function(e, t){
146        // do something
147    },
148
149    // add the same listener to multiple selectors (separated by comma BEFORE the @)
150    '#foo a, #bar span.some-class@mouseover' : function(){
151        // do something
152    }
153 });
154 </code></pre>
155          * @param {Object} obj The list of behaviors to apply
156          */
157         addBehaviors : function(o){
158             if(!Roo.isReady){
159                 Roo.onReady(function(){
160                     Roo.addBehaviors(o);
161                 });
162                 return;
163             }
164             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
165             for(var b in o){
166                 var parts = b.split('@');
167                 if(parts[1]){ // for Object prototype breakers
168                     var s = parts[0];
169                     if(!cache[s]){
170                         cache[s] = Roo.select(s);
171                     }
172                     cache[s].on(parts[1], o[b]);
173                 }
174             }
175             cache = null;
176         },
177
178         /**
179          * Generates unique ids. If the element already has an id, it is unchanged
180          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
181          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
182          * @return {String} The generated Id.
183          */
184         id : function(el, prefix){
185             prefix = prefix || "roo-gen";
186             el = Roo.getDom(el);
187             var id = prefix + (++idSeed);
188             return el ? (el.id ? el.id : (el.id = id)) : id;
189         },
190          
191        
192         /**
193          * Extends one class with another class and optionally overrides members with the passed literal. This class
194          * also adds the function "override()" to the class that can be used to override
195          * members on an instance.
196          * @param {Object} subclass The class inheriting the functionality
197          * @param {Object} superclass The class being extended
198          * @param {Object} overrides (optional) A literal with members
199          * @method extend
200          */
201         extend : function(){
202             // inline overrides
203             var io = function(o){
204                 for(var m in o){
205                     this[m] = o[m];
206                 }
207             };
208             return function(sb, sp, overrides){
209                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
210                     overrides = sp;
211                     sp = sb;
212                     sb = function(){sp.apply(this, arguments);};
213                 }
214                 var F = function(){}, sbp, spp = sp.prototype;
215                 F.prototype = spp;
216                 sbp = sb.prototype = new F();
217                 sbp.constructor=sb;
218                 sb.superclass=spp;
219                 
220                 if(spp.constructor == Object.prototype.constructor){
221                     spp.constructor=sp;
222                    
223                 }
224                 
225                 sb.override = function(o){
226                     Roo.override(sb, o);
227                 };
228                 sbp.override = io;
229                 Roo.override(sb, overrides);
230                 return sb;
231             };
232         }(),
233
234         /**
235          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
236          * Usage:<pre><code>
237 Roo.override(MyClass, {
238     newMethod1: function(){
239         // etc.
240     },
241     newMethod2: function(foo){
242         // etc.
243     }
244 });
245  </code></pre>
246          * @param {Object} origclass The class to override
247          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
248          * containing one or more methods.
249          * @method override
250          */
251         override : function(origclass, overrides){
252             if(overrides){
253                 var p = origclass.prototype;
254                 for(var method in overrides){
255                     p[method] = overrides[method];
256                 }
257             }
258         },
259         /**
260          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
261          * <pre><code>
262 Roo.namespace('Company', 'Company.data');
263 Company.Widget = function() { ... }
264 Company.data.CustomStore = function(config) { ... }
265 </code></pre>
266          * @param {String} namespace1
267          * @param {String} namespace2
268          * @param {String} etc
269          * @method namespace
270          */
271         namespace : function(){
272             var a=arguments, o=null, i, j, d, rt;
273             for (i=0; i<a.length; ++i) {
274                 d=a[i].split(".");
275                 rt = d[0];
276                 /** eval:var:o */
277                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
278                 for (j=1; j<d.length; ++j) {
279                     o[d[j]]=o[d[j]] || {};
280                     o=o[d[j]];
281                 }
282             }
283         },
284         /**
285          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
286          * <pre><code>
287 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
288 Roo.factory(conf, Roo.data);
289 </code></pre>
290          * @param {String} classname
291          * @param {String} namespace (optional)
292          * @method factory
293          */
294          
295         factory : function(c, ns)
296         {
297             // no xtype, no ns or c.xns - or forced off by c.xns
298             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
299                 return c;
300             }
301             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
302             if (c.constructor == ns[c.xtype]) {// already created...
303                 return c;
304             }
305             if (ns[c.xtype]) {
306                 if (Roo.debug) Roo.log("Roo.Factory(" + c.xtype + ")");
307                 var ret = new ns[c.xtype](c);
308                 ret.xns = false;
309                 return ret;
310             }
311             c.xns = false; // prevent recursion..
312             return c;
313         },
314          /**
315          * Logs to console if it can.
316          *
317          * @param {String|Object} string
318          * @method log
319          */
320         log : function(s)
321         {
322             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
323                 return; // alerT?
324             }
325             console.log(s);
326             
327         },
328         /**
329          * Takes an object and converts it to an encoded URL. e.g. Roo.urlEncode({foo: 1, bar: 2}); would return "foo=1&bar=2".  Optionally, property values can be arrays, instead of keys and the resulting string that's returned will contain a name/value pair for each array value.
330          * @param {Object} o
331          * @return {String}
332          */
333         urlEncode : function(o){
334             if(!o){
335                 return "";
336             }
337             var buf = [];
338             for(var key in o){
339                 var ov = o[key], k = Roo.encodeURIComponent(key);
340                 var type = typeof ov;
341                 if(type == 'undefined'){
342                     buf.push(k, "=&");
343                 }else if(type != "function" && type != "object"){
344                     buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
345                 }else if(ov instanceof Array){
346                     if (ov.length) {
347                             for(var i = 0, len = ov.length; i < len; i++) {
348                                 buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
349                             }
350                         } else {
351                             buf.push(k, "=&");
352                         }
353                 }
354             }
355             buf.pop();
356             return buf.join("");
357         },
358          /**
359          * Safe version of encodeURIComponent
360          * @param {String} data 
361          * @return {String} 
362          */
363         
364         encodeURIComponent : function (data)
365         {
366             try {
367                 return encodeURIComponent(data);
368             } catch(e) {} // should be an uri encode error.
369             
370             if (data == '' || data == null){
371                return '';
372             }
373             // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
374             function nibble_to_hex(nibble){
375                 var chars = '0123456789ABCDEF';
376                 return chars.charAt(nibble);
377             }
378             data = data.toString();
379             var buffer = '';
380             for(var i=0; i<data.length; i++){
381                 var c = data.charCodeAt(i);
382                 var bs = new Array();
383                 if (c > 0x10000){
384                         // 4 bytes
385                     bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
386                     bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
387                     bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
388                     bs[3] = 0x80 | (c & 0x3F);
389                 }else if (c > 0x800){
390                          // 3 bytes
391                     bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
392                     bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
393                     bs[2] = 0x80 | (c & 0x3F);
394                 }else if (c > 0x80){
395                        // 2 bytes
396                     bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
397                     bs[1] = 0x80 | (c & 0x3F);
398                 }else{
399                         // 1 byte
400                     bs[0] = c;
401                 }
402                 for(var j=0; j<bs.length; j++){
403                     var b = bs[j];
404                     var hex = nibble_to_hex((b & 0xF0) >>> 4) 
405                             + nibble_to_hex(b &0x0F);
406                     buffer += '%'+hex;
407                }
408             }
409             return buffer;    
410              
411         },
412
413         /**
414          * Takes an encoded URL and and converts it to an object. e.g. Roo.urlDecode("foo=1&bar=2"); would return {foo: 1, bar: 2} or Roo.urlDecode("foo=1&bar=2&bar=3&bar=4", true); would return {foo: 1, bar: [2, 3, 4]}.
415          * @param {String} string
416          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
417          * @return {Object} A literal with members
418          */
419         urlDecode : function(string, overwrite){
420             if(!string || !string.length){
421                 return {};
422             }
423             var obj = {};
424             var pairs = string.split('&');
425             var pair, name, value;
426             for(var i = 0, len = pairs.length; i < len; i++){
427                 pair = pairs[i].split('=');
428                 name = decodeURIComponent(pair[0]);
429                 value = decodeURIComponent(pair[1]);
430                 if(overwrite !== true){
431                     if(typeof obj[name] == "undefined"){
432                         obj[name] = value;
433                     }else if(typeof obj[name] == "string"){
434                         obj[name] = [obj[name]];
435                         obj[name].push(value);
436                     }else{
437                         obj[name].push(value);
438                     }
439                 }else{
440                     obj[name] = value;
441                 }
442             }
443             return obj;
444         },
445
446         /**
447          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
448          * passed array is not really an array, your function is called once with it.
449          * The supplied function is called with (Object item, Number index, Array allItems).
450          * @param {Array/NodeList/Mixed} array
451          * @param {Function} fn
452          * @param {Object} scope
453          */
454         each : function(array, fn, scope){
455             if(typeof array.length == "undefined" || typeof array == "string"){
456                 array = [array];
457             }
458             for(var i = 0, len = array.length; i < len; i++){
459                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
460             }
461         },
462
463         // deprecated
464         combine : function(){
465             var as = arguments, l = as.length, r = [];
466             for(var i = 0; i < l; i++){
467                 var a = as[i];
468                 if(a instanceof Array){
469                     r = r.concat(a);
470                 }else if(a.length !== undefined && !a.substr){
471                     r = r.concat(Array.prototype.slice.call(a, 0));
472                 }else{
473                     r.push(a);
474                 }
475             }
476             return r;
477         },
478
479         /**
480          * Escapes the passed string for use in a regular expression
481          * @param {String} str
482          * @return {String}
483          */
484         escapeRe : function(s) {
485             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
486         },
487
488         // internal
489         callback : function(cb, scope, args, delay){
490             if(typeof cb == "function"){
491                 if(delay){
492                     cb.defer(delay, scope, args || []);
493                 }else{
494                     cb.apply(scope, args || []);
495                 }
496             }
497         },
498
499         /**
500          * Return the dom node for the passed string (id), dom node, or Roo.Element
501          * @param {String/HTMLElement/Roo.Element} el
502          * @return HTMLElement
503          */
504         getDom : function(el){
505             if(!el){
506                 return null;
507             }
508             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
509         },
510
511         /**
512         * Shorthand for {@link Roo.ComponentMgr#get}
513         * @param {String} id
514         * @return Roo.Component
515         */
516         getCmp : function(id){
517             return Roo.ComponentMgr.get(id);
518         },
519          
520         num : function(v, defaultValue){
521             if(typeof v != 'number'){
522                 return defaultValue;
523             }
524             return v;
525         },
526
527         destroy : function(){
528             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
529                 var as = a[i];
530                 if(as){
531                     if(as.dom){
532                         as.removeAllListeners();
533                         as.remove();
534                         continue;
535                     }
536                     if(typeof as.purgeListeners == 'function'){
537                         as.purgeListeners();
538                     }
539                     if(typeof as.destroy == 'function'){
540                         as.destroy();
541                     }
542                 }
543             }
544         },
545
546         // inpired by a similar function in mootools library
547         /**
548          * Returns the type of object that is passed in. If the object passed in is null or undefined it
549          * return false otherwise it returns one of the following values:<ul>
550          * <li><b>string</b>: If the object passed is a string</li>
551          * <li><b>number</b>: If the object passed is a number</li>
552          * <li><b>boolean</b>: If the object passed is a boolean value</li>
553          * <li><b>function</b>: If the object passed is a function reference</li>
554          * <li><b>object</b>: If the object passed is an object</li>
555          * <li><b>array</b>: If the object passed is an array</li>
556          * <li><b>regexp</b>: If the object passed is a regular expression</li>
557          * <li><b>element</b>: If the object passed is a DOM Element</li>
558          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
559          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
560          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
561          * @param {Mixed} object
562          * @return {String}
563          */
564         type : function(o){
565             if(o === undefined || o === null){
566                 return false;
567             }
568             if(o.htmlElement){
569                 return 'element';
570             }
571             var t = typeof o;
572             if(t == 'object' && o.nodeName) {
573                 switch(o.nodeType) {
574                     case 1: return 'element';
575                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
576                 }
577             }
578             if(t == 'object' || t == 'function') {
579                 switch(o.constructor) {
580                     case Array: return 'array';
581                     case RegExp: return 'regexp';
582                 }
583                 if(typeof o.length == 'number' && typeof o.item == 'function') {
584                     return 'nodelist';
585                 }
586             }
587             return t;
588         },
589
590         /**
591          * Returns true if the passed value is null, undefined or an empty string (optional).
592          * @param {Mixed} value The value to test
593          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
594          * @return {Boolean}
595          */
596         isEmpty : function(v, allowBlank){
597             return v === null || v === undefined || (!allowBlank ? v === '' : false);
598         },
599         
600         /** @type Boolean */
601         isOpera : isOpera,
602         /** @type Boolean */
603         isSafari : isSafari,
604         /** @type Boolean */
605         isIE : isIE,
606         /** @type Boolean */
607         isIE7 : isIE7,
608         /** @type Boolean */
609         isGecko : isGecko,
610         /** @type Boolean */
611         isBorderBox : isBorderBox,
612         /** @type Boolean */
613         isWindows : isWindows,
614         /** @type Boolean */
615         isLinux : isLinux,
616         /** @type Boolean */
617         isMac : isMac,
618
619         /**
620          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
621          * you may want to set this to true.
622          * @type Boolean
623          */
624         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
625         
626         
627                 
628         /**
629          * Selects a single element as a Roo Element
630          * This is about as close as you can get to jQuery's $('do crazy stuff')
631          * @param {String} selector The selector/xpath query
632          * @param {Node} root (optional) The start of the query (defaults to document).
633          * @return {Roo.Element}
634          */
635         selectNode : function(selector, root) 
636         {
637             var node = Roo.DomQuery.selectNode(selector,root);
638             return node ? Roo.get(node) : new Roo.Element(false);
639         }
640         
641     });
642
643
644 })();
645
646 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
647                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout", "Roo.app", "Roo.ux");
648 /*
649  * Based on:
650  * Ext JS Library 1.1.1
651  * Copyright(c) 2006-2007, Ext JS, LLC.
652  *
653  * Originally Released Under LGPL - original licence link has changed is not relivant.
654  *
655  * Fork - LGPL
656  * <script type="text/javascript">
657  */
658
659 (function() {    
660     // wrappedn so fnCleanup is not in global scope...
661     if(Roo.isIE) {
662         function fnCleanUp() {
663             var p = Function.prototype;
664             delete p.createSequence;
665             delete p.defer;
666             delete p.createDelegate;
667             delete p.createCallback;
668             delete p.createInterceptor;
669
670             window.detachEvent("onunload", fnCleanUp);
671         }
672         window.attachEvent("onunload", fnCleanUp);
673     }
674 })();
675
676
677 /**
678  * @class Function
679  * These functions are available on every Function object (any JavaScript function).
680  */
681 Roo.apply(Function.prototype, {
682      /**
683      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
684      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
685      * Will create a function that is bound to those 2 args.
686      * @return {Function} The new function
687     */
688     createCallback : function(/*args...*/){
689         // make args available, in function below
690         var args = arguments;
691         var method = this;
692         return function() {
693             return method.apply(window, args);
694         };
695     },
696
697     /**
698      * Creates a delegate (callback) that sets the scope to obj.
699      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
700      * Will create a function that is automatically scoped to this.
701      * @param {Object} obj (optional) The object for which the scope is set
702      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
703      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
704      *                                             if a number the args are inserted at the specified position
705      * @return {Function} The new function
706      */
707     createDelegate : function(obj, args, appendArgs){
708         var method = this;
709         return function() {
710             var callArgs = args || arguments;
711             if(appendArgs === true){
712                 callArgs = Array.prototype.slice.call(arguments, 0);
713                 callArgs = callArgs.concat(args);
714             }else if(typeof appendArgs == "number"){
715                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
716                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
717                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
718             }
719             return method.apply(obj || window, callArgs);
720         };
721     },
722
723     /**
724      * Calls this function after the number of millseconds specified.
725      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
726      * @param {Object} obj (optional) The object for which the scope is set
727      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
728      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
729      *                                             if a number the args are inserted at the specified position
730      * @return {Number} The timeout id that can be used with clearTimeout
731      */
732     defer : function(millis, obj, args, appendArgs){
733         var fn = this.createDelegate(obj, args, appendArgs);
734         if(millis){
735             return setTimeout(fn, millis);
736         }
737         fn();
738         return 0;
739     },
740     /**
741      * Create a combined function call sequence of the original function + the passed function.
742      * The resulting function returns the results of the original function.
743      * The passed fcn is called with the parameters of the original function
744      * @param {Function} fcn The function to sequence
745      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
746      * @return {Function} The new function
747      */
748     createSequence : function(fcn, scope){
749         if(typeof fcn != "function"){
750             return this;
751         }
752         var method = this;
753         return function() {
754             var retval = method.apply(this || window, arguments);
755             fcn.apply(scope || this || window, arguments);
756             return retval;
757         };
758     },
759
760     /**
761      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
762      * The resulting function returns the results of the original function.
763      * The passed fcn is called with the parameters of the original function.
764      * @addon
765      * @param {Function} fcn The function to call before the original
766      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
767      * @return {Function} The new function
768      */
769     createInterceptor : function(fcn, scope){
770         if(typeof fcn != "function"){
771             return this;
772         }
773         var method = this;
774         return function() {
775             fcn.target = this;
776             fcn.method = method;
777             if(fcn.apply(scope || this || window, arguments) === false){
778                 return;
779             }
780             return method.apply(this || window, arguments);
781         };
782     }
783 });
784 /*
785  * Based on:
786  * Ext JS Library 1.1.1
787  * Copyright(c) 2006-2007, Ext JS, LLC.
788  *
789  * Originally Released Under LGPL - original licence link has changed is not relivant.
790  *
791  * Fork - LGPL
792  * <script type="text/javascript">
793  */
794
795 Roo.applyIf(String, {
796     
797     /** @scope String */
798     
799     /**
800      * Escapes the passed string for ' and \
801      * @param {String} string The string to escape
802      * @return {String} The escaped string
803      * @static
804      */
805     escape : function(string) {
806         return string.replace(/('|\\)/g, "\\$1");
807     },
808
809     /**
810      * Pads the left side of a string with a specified character.  This is especially useful
811      * for normalizing number and date strings.  Example usage:
812      * <pre><code>
813 var s = String.leftPad('123', 5, '0');
814 // s now contains the string: '00123'
815 </code></pre>
816      * @param {String} string The original string
817      * @param {Number} size The total length of the output string
818      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
819      * @return {String} The padded string
820      * @static
821      */
822     leftPad : function (val, size, ch) {
823         var result = new String(val);
824         if(ch === null || ch === undefined || ch === '') {
825             ch = " ";
826         }
827         while (result.length < size) {
828             result = ch + result;
829         }
830         return result;
831     },
832
833     /**
834      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
835      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
836      * <pre><code>
837 var cls = 'my-class', text = 'Some text';
838 var s = String.format('<div class="{0}">{1}</div>', cls, text);
839 // s now contains the string: '<div class="my-class">Some text</div>'
840 </code></pre>
841      * @param {String} string The tokenized string to be formatted
842      * @param {String} value1 The value to replace token {0}
843      * @param {String} value2 Etc...
844      * @return {String} The formatted string
845      * @static
846      */
847     format : function(format){
848         var args = Array.prototype.slice.call(arguments, 1);
849         return format.replace(/\{(\d+)\}/g, function(m, i){
850             return Roo.util.Format.htmlEncode(args[i]);
851         });
852     }
853 });
854
855 /**
856  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
857  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
858  * they are already different, the first value passed in is returned.  Note that this method returns the new value
859  * but does not change the current string.
860  * <pre><code>
861 // alternate sort directions
862 sort = sort.toggle('ASC', 'DESC');
863
864 // instead of conditional logic:
865 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
866 </code></pre>
867  * @param {String} value The value to compare to the current string
868  * @param {String} other The new value to use if the string already equals the first value passed in
869  * @return {String} The new value
870  */
871  
872 String.prototype.toggle = function(value, other){
873     return this == value ? other : value;
874 };/*
875  * Based on:
876  * Ext JS Library 1.1.1
877  * Copyright(c) 2006-2007, Ext JS, LLC.
878  *
879  * Originally Released Under LGPL - original licence link has changed is not relivant.
880  *
881  * Fork - LGPL
882  * <script type="text/javascript">
883  */
884
885  /**
886  * @class Number
887  */
888 Roo.applyIf(Number.prototype, {
889     /**
890      * Checks whether or not the current number is within a desired range.  If the number is already within the
891      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
892      * exceeded.  Note that this method returns the constrained value but does not change the current number.
893      * @param {Number} min The minimum number in the range
894      * @param {Number} max The maximum number in the range
895      * @return {Number} The constrained value if outside the range, otherwise the current value
896      */
897     constrain : function(min, max){
898         return Math.min(Math.max(this, min), max);
899     }
900 });/*
901  * Based on:
902  * Ext JS Library 1.1.1
903  * Copyright(c) 2006-2007, Ext JS, LLC.
904  *
905  * Originally Released Under LGPL - original licence link has changed is not relivant.
906  *
907  * Fork - LGPL
908  * <script type="text/javascript">
909  */
910  /**
911  * @class Array
912  */
913 Roo.applyIf(Array.prototype, {
914     /**
915      * Checks whether or not the specified object exists in the array.
916      * @param {Object} o The object to check for
917      * @return {Number} The index of o in the array (or -1 if it is not found)
918      */
919     indexOf : function(o){
920        for (var i = 0, len = this.length; i < len; i++){
921               if(this[i] == o) return i;
922        }
923            return -1;
924     },
925
926     /**
927      * Removes the specified object from the array.  If the object is not found nothing happens.
928      * @param {Object} o The object to remove
929      */
930     remove : function(o){
931        var index = this.indexOf(o);
932        if(index != -1){
933            this.splice(index, 1);
934        }
935     },
936     /**
937      * Map (JS 1.6 compatibility)
938      * @param {Function} function  to call
939      */
940     map : function(fun )
941     {
942         var len = this.length >>> 0;
943         if (typeof fun != "function")
944             throw new TypeError();
945
946         var res = new Array(len);
947         var thisp = arguments[1];
948         for (var i = 0; i < len; i++)
949         {
950             if (i in this)
951                 res[i] = fun.call(thisp, this[i], i, this);
952         }
953
954         return res;
955     }
956     
957 });
958
959
960  /*
961  * Based on:
962  * Ext JS Library 1.1.1
963  * Copyright(c) 2006-2007, Ext JS, LLC.
964  *
965  * Originally Released Under LGPL - original licence link has changed is not relivant.
966  *
967  * Fork - LGPL
968  * <script type="text/javascript">
969  */
970
971 /**
972  * @class Date
973  *
974  * The date parsing and format syntax is a subset of
975  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
976  * supported will provide results equivalent to their PHP versions.
977  *
978  * Following is the list of all currently supported formats:
979  *<pre>
980 Sample date:
981 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
982
983 Format  Output      Description
984 ------  ----------  --------------------------------------------------------------
985   d      10         Day of the month, 2 digits with leading zeros
986   D      Wed        A textual representation of a day, three letters
987   j      10         Day of the month without leading zeros
988   l      Wednesday  A full textual representation of the day of the week
989   S      th         English ordinal day of month suffix, 2 chars (use with j)
990   w      3          Numeric representation of the day of the week
991   z      9          The julian date, or day of the year (0-365)
992   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
993   F      January    A full textual representation of the month
994   m      01         Numeric representation of a month, with leading zeros
995   M      Jan        Month name abbreviation, three letters
996   n      1          Numeric representation of a month, without leading zeros
997   t      31         Number of days in the given month
998   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
999   Y      2007       A full numeric representation of a year, 4 digits
1000   y      07         A two digit representation of a year
1001   a      pm         Lowercase Ante meridiem and Post meridiem
1002   A      PM         Uppercase Ante meridiem and Post meridiem
1003   g      3          12-hour format of an hour without leading zeros
1004   G      15         24-hour format of an hour without leading zeros
1005   h      03         12-hour format of an hour with leading zeros
1006   H      15         24-hour format of an hour with leading zeros
1007   i      05         Minutes with leading zeros
1008   s      01         Seconds, with leading zeros
1009   O      -0600      Difference to Greenwich time (GMT) in hours
1010   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1011   T      CST        Timezone setting of the machine running the code
1012   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1013 </pre>
1014  *
1015  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1016  * <pre><code>
1017 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1018 document.write(dt.format('Y-m-d'));                         //2007-01-10
1019 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1020 document.write(dt.format('l, \\t\\he dS of F Y h:i:s A'));  //Wednesday, the 10th of January 2007 03:05:01 PM
1021  </code></pre>
1022  *
1023  * Here are some standard date/time patterns that you might find helpful.  They
1024  * are not part of the source of Date.js, but to use them you can simply copy this
1025  * block of code into any script that is included after Date.js and they will also become
1026  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1027  * <pre><code>
1028 Date.patterns = {
1029     ISO8601Long:"Y-m-d H:i:s",
1030     ISO8601Short:"Y-m-d",
1031     ShortDate: "n/j/Y",
1032     LongDate: "l, F d, Y",
1033     FullDateTime: "l, F d, Y g:i:s A",
1034     MonthDay: "F d",
1035     ShortTime: "g:i A",
1036     LongTime: "g:i:s A",
1037     SortableDateTime: "Y-m-d\\TH:i:s",
1038     UniversalSortableDateTime: "Y-m-d H:i:sO",
1039     YearMonth: "F, Y"
1040 };
1041 </code></pre>
1042  *
1043  * Example usage:
1044  * <pre><code>
1045 var dt = new Date();
1046 document.write(dt.format(Date.patterns.ShortDate));
1047  </code></pre>
1048  */
1049
1050 /*
1051  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1052  * They generate precompiled functions from date formats instead of parsing and
1053  * processing the pattern every time you format a date.  These functions are available
1054  * on every Date object (any javascript function).
1055  *
1056  * The original article and download are here:
1057  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1058  *
1059  */
1060  
1061  
1062  // was in core
1063 /**
1064  Returns the number of milliseconds between this date and date
1065  @param {Date} date (optional) Defaults to now
1066  @return {Number} The diff in milliseconds
1067  @member Date getElapsed
1068  */
1069 Date.prototype.getElapsed = function(date) {
1070         return Math.abs((date || new Date()).getTime()-this.getTime());
1071 };
1072 // was in date file..
1073
1074
1075 // private
1076 Date.parseFunctions = {count:0};
1077 // private
1078 Date.parseRegexes = [];
1079 // private
1080 Date.formatFunctions = {count:0};
1081
1082 // private
1083 Date.prototype.dateFormat = function(format) {
1084     if (Date.formatFunctions[format] == null) {
1085         Date.createNewFormat(format);
1086     }
1087     var func = Date.formatFunctions[format];
1088     return this[func]();
1089 };
1090
1091
1092 /**
1093  * Formats a date given the supplied format string
1094  * @param {String} format The format string
1095  * @return {String} The formatted date
1096  * @method
1097  */
1098 Date.prototype.format = Date.prototype.dateFormat;
1099
1100 // private
1101 Date.createNewFormat = function(format) {
1102     var funcName = "format" + Date.formatFunctions.count++;
1103     Date.formatFunctions[format] = funcName;
1104     var code = "Date.prototype." + funcName + " = function(){return ";
1105     var special = false;
1106     var ch = '';
1107     for (var i = 0; i < format.length; ++i) {
1108         ch = format.charAt(i);
1109         if (!special && ch == "\\") {
1110             special = true;
1111         }
1112         else if (special) {
1113             special = false;
1114             code += "'" + String.escape(ch) + "' + ";
1115         }
1116         else {
1117             code += Date.getFormatCode(ch);
1118         }
1119     }
1120     /** eval:var:zzzzzzzzzzzzz */
1121     eval(code.substring(0, code.length - 3) + ";}");
1122 };
1123
1124 // private
1125 Date.getFormatCode = function(character) {
1126     switch (character) {
1127     case "d":
1128         return "String.leftPad(this.getDate(), 2, '0') + ";
1129     case "D":
1130         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1131     case "j":
1132         return "this.getDate() + ";
1133     case "l":
1134         return "Date.dayNames[this.getDay()] + ";
1135     case "S":
1136         return "this.getSuffix() + ";
1137     case "w":
1138         return "this.getDay() + ";
1139     case "z":
1140         return "this.getDayOfYear() + ";
1141     case "W":
1142         return "this.getWeekOfYear() + ";
1143     case "F":
1144         return "Date.monthNames[this.getMonth()] + ";
1145     case "m":
1146         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1147     case "M":
1148         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1149     case "n":
1150         return "(this.getMonth() + 1) + ";
1151     case "t":
1152         return "this.getDaysInMonth() + ";
1153     case "L":
1154         return "(this.isLeapYear() ? 1 : 0) + ";
1155     case "Y":
1156         return "this.getFullYear() + ";
1157     case "y":
1158         return "('' + this.getFullYear()).substring(2, 4) + ";
1159     case "a":
1160         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1161     case "A":
1162         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1163     case "g":
1164         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1165     case "G":
1166         return "this.getHours() + ";
1167     case "h":
1168         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1169     case "H":
1170         return "String.leftPad(this.getHours(), 2, '0') + ";
1171     case "i":
1172         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1173     case "s":
1174         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1175     case "O":
1176         return "this.getGMTOffset() + ";
1177     case "P":
1178         return "this.getGMTColonOffset() + ";
1179     case "T":
1180         return "this.getTimezone() + ";
1181     case "Z":
1182         return "(this.getTimezoneOffset() * -60) + ";
1183     default:
1184         return "'" + String.escape(character) + "' + ";
1185     }
1186 };
1187
1188 /**
1189  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1190  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1191  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1192  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1193  * string or the parse operation will fail.
1194  * Example Usage:
1195 <pre><code>
1196 //dt = Fri May 25 2007 (current date)
1197 var dt = new Date();
1198
1199 //dt = Thu May 25 2006 (today's month/day in 2006)
1200 dt = Date.parseDate("2006", "Y");
1201
1202 //dt = Sun Jan 15 2006 (all date parts specified)
1203 dt = Date.parseDate("2006-1-15", "Y-m-d");
1204
1205 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1206 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1207 </code></pre>
1208  * @param {String} input The unparsed date as a string
1209  * @param {String} format The format the date is in
1210  * @return {Date} The parsed date
1211  * @static
1212  */
1213 Date.parseDate = function(input, format) {
1214     if (Date.parseFunctions[format] == null) {
1215         Date.createParser(format);
1216     }
1217     var func = Date.parseFunctions[format];
1218     return Date[func](input);
1219 };
1220 /**
1221  * @private
1222  */
1223 Date.createParser = function(format) {
1224     var funcName = "parse" + Date.parseFunctions.count++;
1225     var regexNum = Date.parseRegexes.length;
1226     var currentGroup = 1;
1227     Date.parseFunctions[format] = funcName;
1228
1229     var code = "Date." + funcName + " = function(input){\n"
1230         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1231         + "var d = new Date();\n"
1232         + "y = d.getFullYear();\n"
1233         + "m = d.getMonth();\n"
1234         + "d = d.getDate();\n"
1235         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1236         + "if (results && results.length > 0) {";
1237     var regex = "";
1238
1239     var special = false;
1240     var ch = '';
1241     for (var i = 0; i < format.length; ++i) {
1242         ch = format.charAt(i);
1243         if (!special && ch == "\\") {
1244             special = true;
1245         }
1246         else if (special) {
1247             special = false;
1248             regex += String.escape(ch);
1249         }
1250         else {
1251             var obj = Date.formatCodeToRegex(ch, currentGroup);
1252             currentGroup += obj.g;
1253             regex += obj.s;
1254             if (obj.g && obj.c) {
1255                 code += obj.c;
1256             }
1257         }
1258     }
1259
1260     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1261         + "{v = new Date(y, m, d, h, i, s);}\n"
1262         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1263         + "{v = new Date(y, m, d, h, i);}\n"
1264         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1265         + "{v = new Date(y, m, d, h);}\n"
1266         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1267         + "{v = new Date(y, m, d);}\n"
1268         + "else if (y >= 0 && m >= 0)\n"
1269         + "{v = new Date(y, m);}\n"
1270         + "else if (y >= 0)\n"
1271         + "{v = new Date(y);}\n"
1272         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1273         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1274         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1275         + ";}";
1276
1277     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1278     /** eval:var:zzzzzzzzzzzzz */
1279     eval(code);
1280 };
1281
1282 // private
1283 Date.formatCodeToRegex = function(character, currentGroup) {
1284     switch (character) {
1285     case "D":
1286         return {g:0,
1287         c:null,
1288         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1289     case "j":
1290         return {g:1,
1291             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1292             s:"(\\d{1,2})"}; // day of month without leading zeroes
1293     case "d":
1294         return {g:1,
1295             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1296             s:"(\\d{2})"}; // day of month with leading zeroes
1297     case "l":
1298         return {g:0,
1299             c:null,
1300             s:"(?:" + Date.dayNames.join("|") + ")"};
1301     case "S":
1302         return {g:0,
1303             c:null,
1304             s:"(?:st|nd|rd|th)"};
1305     case "w":
1306         return {g:0,
1307             c:null,
1308             s:"\\d"};
1309     case "z":
1310         return {g:0,
1311             c:null,
1312             s:"(?:\\d{1,3})"};
1313     case "W":
1314         return {g:0,
1315             c:null,
1316             s:"(?:\\d{2})"};
1317     case "F":
1318         return {g:1,
1319             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1320             s:"(" + Date.monthNames.join("|") + ")"};
1321     case "M":
1322         return {g:1,
1323             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1324             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1325     case "n":
1326         return {g:1,
1327             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1328             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1329     case "m":
1330         return {g:1,
1331             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1332             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1333     case "t":
1334         return {g:0,
1335             c:null,
1336             s:"\\d{1,2}"};
1337     case "L":
1338         return {g:0,
1339             c:null,
1340             s:"(?:1|0)"};
1341     case "Y":
1342         return {g:1,
1343             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1344             s:"(\\d{4})"};
1345     case "y":
1346         return {g:1,
1347             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1348                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1349             s:"(\\d{1,2})"};
1350     case "a":
1351         return {g:1,
1352             c:"if (results[" + currentGroup + "] == 'am') {\n"
1353                 + "if (h == 12) { h = 0; }\n"
1354                 + "} else { if (h < 12) { h += 12; }}",
1355             s:"(am|pm)"};
1356     case "A":
1357         return {g:1,
1358             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1359                 + "if (h == 12) { h = 0; }\n"
1360                 + "} else { if (h < 12) { h += 12; }}",
1361             s:"(AM|PM)"};
1362     case "g":
1363     case "G":
1364         return {g:1,
1365             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1366             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1367     case "h":
1368     case "H":
1369         return {g:1,
1370             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1371             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1372     case "i":
1373         return {g:1,
1374             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1375             s:"(\\d{2})"};
1376     case "s":
1377         return {g:1,
1378             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1379             s:"(\\d{2})"};
1380     case "O":
1381         return {g:1,
1382             c:[
1383                 "o = results[", currentGroup, "];\n",
1384                 "var sn = o.substring(0,1);\n", // get + / - sign
1385                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1386                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1387                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1388                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1389             ].join(""),
1390             s:"([+\-]\\d{4})"};
1391     case "P":
1392         return {g:1,
1393                 c:[
1394                    "o = results[", currentGroup, "];\n",
1395                    "var sn = o.substring(0,1);\n",
1396                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1397                    "var mn = o.substring(4,6) % 60;\n",
1398                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1399                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1400             ].join(""),
1401             s:"([+\-]\\d{4})"};
1402     case "T":
1403         return {g:0,
1404             c:null,
1405             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1406     case "Z":
1407         return {g:1,
1408             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1409                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1410             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1411     default:
1412         return {g:0,
1413             c:null,
1414             s:String.escape(character)};
1415     }
1416 };
1417
1418 /**
1419  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1420  * @return {String} The abbreviated timezone name (e.g. 'CST')
1421  */
1422 Date.prototype.getTimezone = function() {
1423     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1424 };
1425
1426 /**
1427  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1428  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1429  */
1430 Date.prototype.getGMTOffset = function() {
1431     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1432         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1433         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1434 };
1435
1436 /**
1437  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1438  * @return {String} 2-characters representing hours and 2-characters representing minutes
1439  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1440  */
1441 Date.prototype.getGMTColonOffset = function() {
1442         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1443                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1444                 + ":"
1445                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1446 }
1447
1448 /**
1449  * Get the numeric day number of the year, adjusted for leap year.
1450  * @return {Number} 0 through 364 (365 in leap years)
1451  */
1452 Date.prototype.getDayOfYear = function() {
1453     var num = 0;
1454     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1455     for (var i = 0; i < this.getMonth(); ++i) {
1456         num += Date.daysInMonth[i];
1457     }
1458     return num + this.getDate() - 1;
1459 };
1460
1461 /**
1462  * Get the string representation of the numeric week number of the year
1463  * (equivalent to the format specifier 'W').
1464  * @return {String} '00' through '52'
1465  */
1466 Date.prototype.getWeekOfYear = function() {
1467     // Skip to Thursday of this week
1468     var now = this.getDayOfYear() + (4 - this.getDay());
1469     // Find the first Thursday of the year
1470     var jan1 = new Date(this.getFullYear(), 0, 1);
1471     var then = (7 - jan1.getDay() + 4);
1472     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1473 };
1474
1475 /**
1476  * Whether or not the current date is in a leap year.
1477  * @return {Boolean} True if the current date is in a leap year, else false
1478  */
1479 Date.prototype.isLeapYear = function() {
1480     var year = this.getFullYear();
1481     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1482 };
1483
1484 /**
1485  * Get the first day of the current month, adjusted for leap year.  The returned value
1486  * is the numeric day index within the week (0-6) which can be used in conjunction with
1487  * the {@link #monthNames} array to retrieve the textual day name.
1488  * Example:
1489  *<pre><code>
1490 var dt = new Date('1/10/2007');
1491 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1492 </code></pre>
1493  * @return {Number} The day number (0-6)
1494  */
1495 Date.prototype.getFirstDayOfMonth = function() {
1496     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1497     return (day < 0) ? (day + 7) : day;
1498 };
1499
1500 /**
1501  * Get the last day of the current month, adjusted for leap year.  The returned value
1502  * is the numeric day index within the week (0-6) which can be used in conjunction with
1503  * the {@link #monthNames} array to retrieve the textual day name.
1504  * Example:
1505  *<pre><code>
1506 var dt = new Date('1/10/2007');
1507 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1508 </code></pre>
1509  * @return {Number} The day number (0-6)
1510  */
1511 Date.prototype.getLastDayOfMonth = function() {
1512     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1513     return (day < 0) ? (day + 7) : day;
1514 };
1515
1516
1517 /**
1518  * Get the first date of this date's month
1519  * @return {Date}
1520  */
1521 Date.prototype.getFirstDateOfMonth = function() {
1522     return new Date(this.getFullYear(), this.getMonth(), 1);
1523 };
1524
1525 /**
1526  * Get the last date of this date's month
1527  * @return {Date}
1528  */
1529 Date.prototype.getLastDateOfMonth = function() {
1530     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1531 };
1532 /**
1533  * Get the number of days in the current month, adjusted for leap year.
1534  * @return {Number} The number of days in the month
1535  */
1536 Date.prototype.getDaysInMonth = function() {
1537     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1538     return Date.daysInMonth[this.getMonth()];
1539 };
1540
1541 /**
1542  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1543  * @return {String} 'st, 'nd', 'rd' or 'th'
1544  */
1545 Date.prototype.getSuffix = function() {
1546     switch (this.getDate()) {
1547         case 1:
1548         case 21:
1549         case 31:
1550             return "st";
1551         case 2:
1552         case 22:
1553             return "nd";
1554         case 3:
1555         case 23:
1556             return "rd";
1557         default:
1558             return "th";
1559     }
1560 };
1561
1562 // private
1563 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1564
1565 /**
1566  * An array of textual month names.
1567  * Override these values for international dates, for example...
1568  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1569  * @type Array
1570  * @static
1571  */
1572 Date.monthNames =
1573    ["January",
1574     "February",
1575     "March",
1576     "April",
1577     "May",
1578     "June",
1579     "July",
1580     "August",
1581     "September",
1582     "October",
1583     "November",
1584     "December"];
1585
1586 /**
1587  * An array of textual day names.
1588  * Override these values for international dates, for example...
1589  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1590  * @type Array
1591  * @static
1592  */
1593 Date.dayNames =
1594    ["Sunday",
1595     "Monday",
1596     "Tuesday",
1597     "Wednesday",
1598     "Thursday",
1599     "Friday",
1600     "Saturday"];
1601
1602 // private
1603 Date.y2kYear = 50;
1604 // private
1605 Date.monthNumbers = {
1606     Jan:0,
1607     Feb:1,
1608     Mar:2,
1609     Apr:3,
1610     May:4,
1611     Jun:5,
1612     Jul:6,
1613     Aug:7,
1614     Sep:8,
1615     Oct:9,
1616     Nov:10,
1617     Dec:11};
1618
1619 /**
1620  * Creates and returns a new Date instance with the exact same date value as the called instance.
1621  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1622  * variable will also be changed.  When the intention is to create a new variable that will not
1623  * modify the original instance, you should create a clone.
1624  *
1625  * Example of correctly cloning a date:
1626  * <pre><code>
1627 //wrong way:
1628 var orig = new Date('10/1/2006');
1629 var copy = orig;
1630 copy.setDate(5);
1631 document.write(orig);  //returns 'Thu Oct 05 2006'!
1632
1633 //correct way:
1634 var orig = new Date('10/1/2006');
1635 var copy = orig.clone();
1636 copy.setDate(5);
1637 document.write(orig);  //returns 'Thu Oct 01 2006'
1638 </code></pre>
1639  * @return {Date} The new Date instance
1640  */
1641 Date.prototype.clone = function() {
1642         return new Date(this.getTime());
1643 };
1644
1645 /**
1646  * Clears any time information from this date
1647  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1648  @return {Date} this or the clone
1649  */
1650 Date.prototype.clearTime = function(clone){
1651     if(clone){
1652         return this.clone().clearTime();
1653     }
1654     this.setHours(0);
1655     this.setMinutes(0);
1656     this.setSeconds(0);
1657     this.setMilliseconds(0);
1658     return this;
1659 };
1660
1661 // private
1662 // safari setMonth is broken
1663 if(Roo.isSafari){
1664     Date.brokenSetMonth = Date.prototype.setMonth;
1665         Date.prototype.setMonth = function(num){
1666                 if(num <= -1){
1667                         var n = Math.ceil(-num);
1668                         var back_year = Math.ceil(n/12);
1669                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1670                         this.setFullYear(this.getFullYear() - back_year);
1671                         return Date.brokenSetMonth.call(this, month);
1672                 } else {
1673                         return Date.brokenSetMonth.apply(this, arguments);
1674                 }
1675         };
1676 }
1677
1678 /** Date interval constant 
1679 * @static 
1680 * @type String */
1681 Date.MILLI = "ms";
1682 /** Date interval constant 
1683 * @static 
1684 * @type String */
1685 Date.SECOND = "s";
1686 /** Date interval constant 
1687 * @static 
1688 * @type String */
1689 Date.MINUTE = "mi";
1690 /** Date interval constant 
1691 * @static 
1692 * @type String */
1693 Date.HOUR = "h";
1694 /** Date interval constant 
1695 * @static 
1696 * @type String */
1697 Date.DAY = "d";
1698 /** Date interval constant 
1699 * @static 
1700 * @type String */
1701 Date.MONTH = "mo";
1702 /** Date interval constant 
1703 * @static 
1704 * @type String */
1705 Date.YEAR = "y";
1706
1707 /**
1708  * Provides a convenient method of performing basic date arithmetic.  This method
1709  * does not modify the Date instance being called - it creates and returns
1710  * a new Date instance containing the resulting date value.
1711  *
1712  * Examples:
1713  * <pre><code>
1714 //Basic usage:
1715 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1716 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1717
1718 //Negative values will subtract correctly:
1719 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1720 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1721
1722 //You can even chain several calls together in one line!
1723 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1724 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1725  </code></pre>
1726  *
1727  * @param {String} interval   A valid date interval enum value
1728  * @param {Number} value      The amount to add to the current date
1729  * @return {Date} The new Date instance
1730  */
1731 Date.prototype.add = function(interval, value){
1732   var d = this.clone();
1733   if (!interval || value === 0) return d;
1734   switch(interval.toLowerCase()){
1735     case Date.MILLI:
1736       d.setMilliseconds(this.getMilliseconds() + value);
1737       break;
1738     case Date.SECOND:
1739       d.setSeconds(this.getSeconds() + value);
1740       break;
1741     case Date.MINUTE:
1742       d.setMinutes(this.getMinutes() + value);
1743       break;
1744     case Date.HOUR:
1745       d.setHours(this.getHours() + value);
1746       break;
1747     case Date.DAY:
1748       d.setDate(this.getDate() + value);
1749       break;
1750     case Date.MONTH:
1751       var day = this.getDate();
1752       if(day > 28){
1753           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1754       }
1755       d.setDate(day);
1756       d.setMonth(this.getMonth() + value);
1757       break;
1758     case Date.YEAR:
1759       d.setFullYear(this.getFullYear() + value);
1760       break;
1761   }
1762   return d;
1763 };
1764 /*
1765  * Based on:
1766  * Ext JS Library 1.1.1
1767  * Copyright(c) 2006-2007, Ext JS, LLC.
1768  *
1769  * Originally Released Under LGPL - original licence link has changed is not relivant.
1770  *
1771  * Fork - LGPL
1772  * <script type="text/javascript">
1773  */
1774
1775 Roo.lib.Dom = {
1776     getViewWidth : function(full) {
1777         return full ? this.getDocumentWidth() : this.getViewportWidth();
1778     },
1779
1780     getViewHeight : function(full) {
1781         return full ? this.getDocumentHeight() : this.getViewportHeight();
1782     },
1783
1784     getDocumentHeight: function() {
1785         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1786         return Math.max(scrollHeight, this.getViewportHeight());
1787     },
1788
1789     getDocumentWidth: function() {
1790         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1791         return Math.max(scrollWidth, this.getViewportWidth());
1792     },
1793
1794     getViewportHeight: function() {
1795         var height = self.innerHeight;
1796         var mode = document.compatMode;
1797
1798         if ((mode || Roo.isIE) && !Roo.isOpera) {
1799             height = (mode == "CSS1Compat") ?
1800                      document.documentElement.clientHeight :
1801                      document.body.clientHeight;
1802         }
1803
1804         return height;
1805     },
1806
1807     getViewportWidth: function() {
1808         var width = self.innerWidth;
1809         var mode = document.compatMode;
1810
1811         if (mode || Roo.isIE) {
1812             width = (mode == "CSS1Compat") ?
1813                     document.documentElement.clientWidth :
1814                     document.body.clientWidth;
1815         }
1816         return width;
1817     },
1818
1819     isAncestor : function(p, c) {
1820         p = Roo.getDom(p);
1821         c = Roo.getDom(c);
1822         if (!p || !c) {
1823             return false;
1824         }
1825
1826         if (p.contains && !Roo.isSafari) {
1827             return p.contains(c);
1828         } else if (p.compareDocumentPosition) {
1829             return !!(p.compareDocumentPosition(c) & 16);
1830         } else {
1831             var parent = c.parentNode;
1832             while (parent) {
1833                 if (parent == p) {
1834                     return true;
1835                 }
1836                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1837                     return false;
1838                 }
1839                 parent = parent.parentNode;
1840             }
1841             return false;
1842         }
1843     },
1844
1845     getRegion : function(el) {
1846         return Roo.lib.Region.getRegion(el);
1847     },
1848
1849     getY : function(el) {
1850         return this.getXY(el)[1];
1851     },
1852
1853     getX : function(el) {
1854         return this.getXY(el)[0];
1855     },
1856
1857     getXY : function(el) {
1858         var p, pe, b, scroll, bd = document.body;
1859         el = Roo.getDom(el);
1860         var fly = Roo.lib.AnimBase.fly;
1861         if (el.getBoundingClientRect) {
1862             b = el.getBoundingClientRect();
1863             scroll = fly(document).getScroll();
1864             return [b.left + scroll.left, b.top + scroll.top];
1865         }
1866         var x = 0, y = 0;
1867
1868         p = el;
1869
1870         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1871
1872         while (p) {
1873
1874             x += p.offsetLeft;
1875             y += p.offsetTop;
1876
1877             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1878                 hasAbsolute = true;
1879             }
1880
1881             if (Roo.isGecko) {
1882                 pe = fly(p);
1883
1884                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1885                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1886
1887
1888                 x += bl;
1889                 y += bt;
1890
1891
1892                 if (p != el && pe.getStyle('overflow') != 'visible') {
1893                     x += bl;
1894                     y += bt;
1895                 }
1896             }
1897             p = p.offsetParent;
1898         }
1899
1900         if (Roo.isSafari && hasAbsolute) {
1901             x -= bd.offsetLeft;
1902             y -= bd.offsetTop;
1903         }
1904
1905         if (Roo.isGecko && !hasAbsolute) {
1906             var dbd = fly(bd);
1907             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
1908             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
1909         }
1910
1911         p = el.parentNode;
1912         while (p && p != bd) {
1913             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
1914                 x -= p.scrollLeft;
1915                 y -= p.scrollTop;
1916             }
1917             p = p.parentNode;
1918         }
1919         return [x, y];
1920     },
1921  
1922   
1923
1924
1925     setXY : function(el, xy) {
1926         el = Roo.fly(el, '_setXY');
1927         el.position();
1928         var pts = el.translatePoints(xy);
1929         if (xy[0] !== false) {
1930             el.dom.style.left = pts.left + "px";
1931         }
1932         if (xy[1] !== false) {
1933             el.dom.style.top = pts.top + "px";
1934         }
1935     },
1936
1937     setX : function(el, x) {
1938         this.setXY(el, [x, false]);
1939     },
1940
1941     setY : function(el, y) {
1942         this.setXY(el, [false, y]);
1943     }
1944 };
1945 /*
1946  * Portions of this file are based on pieces of Yahoo User Interface Library
1947  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
1948  * YUI licensed under the BSD License:
1949  * http://developer.yahoo.net/yui/license.txt
1950  * <script type="text/javascript">
1951  *
1952  */
1953
1954 Roo.lib.Event = function() {
1955     var loadComplete = false;
1956     var listeners = [];
1957     var unloadListeners = [];
1958     var retryCount = 0;
1959     var onAvailStack = [];
1960     var counter = 0;
1961     var lastError = null;
1962
1963     return {
1964         POLL_RETRYS: 200,
1965         POLL_INTERVAL: 20,
1966         EL: 0,
1967         TYPE: 1,
1968         FN: 2,
1969         WFN: 3,
1970         OBJ: 3,
1971         ADJ_SCOPE: 4,
1972         _interval: null,
1973
1974         startInterval: function() {
1975             if (!this._interval) {
1976                 var self = this;
1977                 var callback = function() {
1978                     self._tryPreloadAttach();
1979                 };
1980                 this._interval = setInterval(callback, this.POLL_INTERVAL);
1981
1982             }
1983         },
1984
1985         onAvailable: function(p_id, p_fn, p_obj, p_override) {
1986             onAvailStack.push({ id:         p_id,
1987                 fn:         p_fn,
1988                 obj:        p_obj,
1989                 override:   p_override,
1990                 checkReady: false    });
1991
1992             retryCount = this.POLL_RETRYS;
1993             this.startInterval();
1994         },
1995
1996
1997         addListener: function(el, eventName, fn) {
1998             el = Roo.getDom(el);
1999             if (!el || !fn) {
2000                 return false;
2001             }
2002
2003             if ("unload" == eventName) {
2004                 unloadListeners[unloadListeners.length] =
2005                 [el, eventName, fn];
2006                 return true;
2007             }
2008
2009             var wrappedFn = function(e) {
2010                 return fn(Roo.lib.Event.getEvent(e));
2011             };
2012
2013             var li = [el, eventName, fn, wrappedFn];
2014
2015             var index = listeners.length;
2016             listeners[index] = li;
2017
2018             this.doAdd(el, eventName, wrappedFn, false);
2019             return true;
2020
2021         },
2022
2023
2024         removeListener: function(el, eventName, fn) {
2025             var i, len;
2026
2027             el = Roo.getDom(el);
2028
2029             if(!fn) {
2030                 return this.purgeElement(el, false, eventName);
2031             }
2032
2033
2034             if ("unload" == eventName) {
2035
2036                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2037                     var li = unloadListeners[i];
2038                     if (li &&
2039                         li[0] == el &&
2040                         li[1] == eventName &&
2041                         li[2] == fn) {
2042                         unloadListeners.splice(i, 1);
2043                         return true;
2044                     }
2045                 }
2046
2047                 return false;
2048             }
2049
2050             var cacheItem = null;
2051
2052
2053             var index = arguments[3];
2054
2055             if ("undefined" == typeof index) {
2056                 index = this._getCacheIndex(el, eventName, fn);
2057             }
2058
2059             if (index >= 0) {
2060                 cacheItem = listeners[index];
2061             }
2062
2063             if (!el || !cacheItem) {
2064                 return false;
2065             }
2066
2067             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2068
2069             delete listeners[index][this.WFN];
2070             delete listeners[index][this.FN];
2071             listeners.splice(index, 1);
2072
2073             return true;
2074
2075         },
2076
2077
2078         getTarget: function(ev, resolveTextNode) {
2079             ev = ev.browserEvent || ev;
2080             var t = ev.target || ev.srcElement;
2081             return this.resolveTextNode(t);
2082         },
2083
2084
2085         resolveTextNode: function(node) {
2086             if (Roo.isSafari && node && 3 == node.nodeType) {
2087                 return node.parentNode;
2088             } else {
2089                 return node;
2090             }
2091         },
2092
2093
2094         getPageX: function(ev) {
2095             ev = ev.browserEvent || ev;
2096             var x = ev.pageX;
2097             if (!x && 0 !== x) {
2098                 x = ev.clientX || 0;
2099
2100                 if (Roo.isIE) {
2101                     x += this.getScroll()[1];
2102                 }
2103             }
2104
2105             return x;
2106         },
2107
2108
2109         getPageY: function(ev) {
2110             ev = ev.browserEvent || ev;
2111             var y = ev.pageY;
2112             if (!y && 0 !== y) {
2113                 y = ev.clientY || 0;
2114
2115                 if (Roo.isIE) {
2116                     y += this.getScroll()[0];
2117                 }
2118             }
2119
2120
2121             return y;
2122         },
2123
2124
2125         getXY: function(ev) {
2126             ev = ev.browserEvent || ev;
2127             return [this.getPageX(ev), this.getPageY(ev)];
2128         },
2129
2130
2131         getRelatedTarget: function(ev) {
2132             ev = ev.browserEvent || ev;
2133             var t = ev.relatedTarget;
2134             if (!t) {
2135                 if (ev.type == "mouseout") {
2136                     t = ev.toElement;
2137                 } else if (ev.type == "mouseover") {
2138                     t = ev.fromElement;
2139                 }
2140             }
2141
2142             return this.resolveTextNode(t);
2143         },
2144
2145
2146         getTime: function(ev) {
2147             ev = ev.browserEvent || ev;
2148             if (!ev.time) {
2149                 var t = new Date().getTime();
2150                 try {
2151                     ev.time = t;
2152                 } catch(ex) {
2153                     this.lastError = ex;
2154                     return t;
2155                 }
2156             }
2157
2158             return ev.time;
2159         },
2160
2161
2162         stopEvent: function(ev) {
2163             this.stopPropagation(ev);
2164             this.preventDefault(ev);
2165         },
2166
2167
2168         stopPropagation: function(ev) {
2169             ev = ev.browserEvent || ev;
2170             if (ev.stopPropagation) {
2171                 ev.stopPropagation();
2172             } else {
2173                 ev.cancelBubble = true;
2174             }
2175         },
2176
2177
2178         preventDefault: function(ev) {
2179             ev = ev.browserEvent || ev;
2180             if(ev.preventDefault) {
2181                 ev.preventDefault();
2182             } else {
2183                 ev.returnValue = false;
2184             }
2185         },
2186
2187
2188         getEvent: function(e) {
2189             var ev = e || window.event;
2190             if (!ev) {
2191                 var c = this.getEvent.caller;
2192                 while (c) {
2193                     ev = c.arguments[0];
2194                     if (ev && Event == ev.constructor) {
2195                         break;
2196                     }
2197                     c = c.caller;
2198                 }
2199             }
2200             return ev;
2201         },
2202
2203
2204         getCharCode: function(ev) {
2205             ev = ev.browserEvent || ev;
2206             return ev.charCode || ev.keyCode || 0;
2207         },
2208
2209
2210         _getCacheIndex: function(el, eventName, fn) {
2211             for (var i = 0,len = listeners.length; i < len; ++i) {
2212                 var li = listeners[i];
2213                 if (li &&
2214                     li[this.FN] == fn &&
2215                     li[this.EL] == el &&
2216                     li[this.TYPE] == eventName) {
2217                     return i;
2218                 }
2219             }
2220
2221             return -1;
2222         },
2223
2224
2225         elCache: {},
2226
2227
2228         getEl: function(id) {
2229             return document.getElementById(id);
2230         },
2231
2232
2233         clearCache: function() {
2234         },
2235
2236
2237         _load: function(e) {
2238             loadComplete = true;
2239             var EU = Roo.lib.Event;
2240
2241
2242             if (Roo.isIE) {
2243                 EU.doRemove(window, "load", EU._load);
2244             }
2245         },
2246
2247
2248         _tryPreloadAttach: function() {
2249
2250             if (this.locked) {
2251                 return false;
2252             }
2253
2254             this.locked = true;
2255
2256
2257             var tryAgain = !loadComplete;
2258             if (!tryAgain) {
2259                 tryAgain = (retryCount > 0);
2260             }
2261
2262
2263             var notAvail = [];
2264             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2265                 var item = onAvailStack[i];
2266                 if (item) {
2267                     var el = this.getEl(item.id);
2268
2269                     if (el) {
2270                         if (!item.checkReady ||
2271                             loadComplete ||
2272                             el.nextSibling ||
2273                             (document && document.body)) {
2274
2275                             var scope = el;
2276                             if (item.override) {
2277                                 if (item.override === true) {
2278                                     scope = item.obj;
2279                                 } else {
2280                                     scope = item.override;
2281                                 }
2282                             }
2283                             item.fn.call(scope, item.obj);
2284                             onAvailStack[i] = null;
2285                         }
2286                     } else {
2287                         notAvail.push(item);
2288                     }
2289                 }
2290             }
2291
2292             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2293
2294             if (tryAgain) {
2295
2296                 this.startInterval();
2297             } else {
2298                 clearInterval(this._interval);
2299                 this._interval = null;
2300             }
2301
2302             this.locked = false;
2303
2304             return true;
2305
2306         },
2307
2308
2309         purgeElement: function(el, recurse, eventName) {
2310             var elListeners = this.getListeners(el, eventName);
2311             if (elListeners) {
2312                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2313                     var l = elListeners[i];
2314                     this.removeListener(el, l.type, l.fn);
2315                 }
2316             }
2317
2318             if (recurse && el && el.childNodes) {
2319                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2320                     this.purgeElement(el.childNodes[i], recurse, eventName);
2321                 }
2322             }
2323         },
2324
2325
2326         getListeners: function(el, eventName) {
2327             var results = [], searchLists;
2328             if (!eventName) {
2329                 searchLists = [listeners, unloadListeners];
2330             } else if (eventName == "unload") {
2331                 searchLists = [unloadListeners];
2332             } else {
2333                 searchLists = [listeners];
2334             }
2335
2336             for (var j = 0; j < searchLists.length; ++j) {
2337                 var searchList = searchLists[j];
2338                 if (searchList && searchList.length > 0) {
2339                     for (var i = 0,len = searchList.length; i < len; ++i) {
2340                         var l = searchList[i];
2341                         if (l && l[this.EL] === el &&
2342                             (!eventName || eventName === l[this.TYPE])) {
2343                             results.push({
2344                                 type:   l[this.TYPE],
2345                                 fn:     l[this.FN],
2346                                 obj:    l[this.OBJ],
2347                                 adjust: l[this.ADJ_SCOPE],
2348                                 index:  i
2349                             });
2350                         }
2351                     }
2352                 }
2353             }
2354
2355             return (results.length) ? results : null;
2356         },
2357
2358
2359         _unload: function(e) {
2360
2361             var EU = Roo.lib.Event, i, j, l, len, index;
2362
2363             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2364                 l = unloadListeners[i];
2365                 if (l) {
2366                     var scope = window;
2367                     if (l[EU.ADJ_SCOPE]) {
2368                         if (l[EU.ADJ_SCOPE] === true) {
2369                             scope = l[EU.OBJ];
2370                         } else {
2371                             scope = l[EU.ADJ_SCOPE];
2372                         }
2373                     }
2374                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2375                     unloadListeners[i] = null;
2376                     l = null;
2377                     scope = null;
2378                 }
2379             }
2380
2381             unloadListeners = null;
2382
2383             if (listeners && listeners.length > 0) {
2384                 j = listeners.length;
2385                 while (j) {
2386                     index = j - 1;
2387                     l = listeners[index];
2388                     if (l) {
2389                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2390                                 l[EU.FN], index);
2391                     }
2392                     j = j - 1;
2393                 }
2394                 l = null;
2395
2396                 EU.clearCache();
2397             }
2398
2399             EU.doRemove(window, "unload", EU._unload);
2400
2401         },
2402
2403
2404         getScroll: function() {
2405             var dd = document.documentElement, db = document.body;
2406             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2407                 return [dd.scrollTop, dd.scrollLeft];
2408             } else if (db) {
2409                 return [db.scrollTop, db.scrollLeft];
2410             } else {
2411                 return [0, 0];
2412             }
2413         },
2414
2415
2416         doAdd: function () {
2417             if (window.addEventListener) {
2418                 return function(el, eventName, fn, capture) {
2419                     el.addEventListener(eventName, fn, (capture));
2420                 };
2421             } else if (window.attachEvent) {
2422                 return function(el, eventName, fn, capture) {
2423                     el.attachEvent("on" + eventName, fn);
2424                 };
2425             } else {
2426                 return function() {
2427                 };
2428             }
2429         }(),
2430
2431
2432         doRemove: function() {
2433             if (window.removeEventListener) {
2434                 return function (el, eventName, fn, capture) {
2435                     el.removeEventListener(eventName, fn, (capture));
2436                 };
2437             } else if (window.detachEvent) {
2438                 return function (el, eventName, fn) {
2439                     el.detachEvent("on" + eventName, fn);
2440                 };
2441             } else {
2442                 return function() {
2443                 };
2444             }
2445         }()
2446     };
2447     
2448 }();
2449 (function() {     
2450    
2451     var E = Roo.lib.Event;
2452     E.on = E.addListener;
2453     E.un = E.removeListener;
2454
2455     if (document && document.body) {
2456         E._load();
2457     } else {
2458         E.doAdd(window, "load", E._load);
2459     }
2460     E.doAdd(window, "unload", E._unload);
2461     E._tryPreloadAttach();
2462 })();
2463
2464 /*
2465  * Portions of this file are based on pieces of Yahoo User Interface Library
2466  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2467  * YUI licensed under the BSD License:
2468  * http://developer.yahoo.net/yui/license.txt
2469  * <script type="text/javascript">
2470  *
2471  */
2472
2473 (function() {
2474     /**
2475      * @class Roo.lib.Ajax
2476      *
2477      */
2478     Roo.lib.Ajax = {
2479         /**
2480          * @static 
2481          */
2482         request : function(method, uri, cb, data, options) {
2483             if(options){
2484                 var hs = options.headers;
2485                 if(hs){
2486                     for(var h in hs){
2487                         if(hs.hasOwnProperty(h)){
2488                             this.initHeader(h, hs[h], false);
2489                         }
2490                     }
2491                 }
2492                 if(options.xmlData){
2493                     this.initHeader('Content-Type', 'text/xml', false);
2494                     method = 'POST';
2495                     data = options.xmlData;
2496                 }
2497             }
2498
2499             return this.asyncRequest(method, uri, cb, data);
2500         },
2501
2502         serializeForm : function(form) {
2503             if(typeof form == 'string') {
2504                 form = (document.getElementById(form) || document.forms[form]);
2505             }
2506
2507             var el, name, val, disabled, data = '', hasSubmit = false;
2508             for (var i = 0; i < form.elements.length; i++) {
2509                 el = form.elements[i];
2510                 disabled = form.elements[i].disabled;
2511                 name = form.elements[i].name;
2512                 val = form.elements[i].value;
2513
2514                 if (!disabled && name){
2515                     switch (el.type)
2516                             {
2517                         case 'select-one':
2518                         case 'select-multiple':
2519                             for (var j = 0; j < el.options.length; j++) {
2520                                 if (el.options[j].selected) {
2521                                     if (Roo.isIE) {
2522                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2523                                     }
2524                                     else {
2525                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2526                                     }
2527                                 }
2528                             }
2529                             break;
2530                         case 'radio':
2531                         case 'checkbox':
2532                             if (el.checked) {
2533                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2534                             }
2535                             break;
2536                         case 'file':
2537
2538                         case undefined:
2539
2540                         case 'reset':
2541
2542                         case 'button':
2543
2544                             break;
2545                         case 'submit':
2546                             if(hasSubmit == false) {
2547                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2548                                 hasSubmit = true;
2549                             }
2550                             break;
2551                         default:
2552                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2553                             break;
2554                     }
2555                 }
2556             }
2557             data = data.substr(0, data.length - 1);
2558             return data;
2559         },
2560
2561         headers:{},
2562
2563         hasHeaders:false,
2564
2565         useDefaultHeader:true,
2566
2567         defaultPostHeader:'application/x-www-form-urlencoded',
2568
2569         useDefaultXhrHeader:true,
2570
2571         defaultXhrHeader:'XMLHttpRequest',
2572
2573         hasDefaultHeaders:true,
2574
2575         defaultHeaders:{},
2576
2577         poll:{},
2578
2579         timeout:{},
2580
2581         pollInterval:50,
2582
2583         transactionId:0,
2584
2585         setProgId:function(id)
2586         {
2587             this.activeX.unshift(id);
2588         },
2589
2590         setDefaultPostHeader:function(b)
2591         {
2592             this.useDefaultHeader = b;
2593         },
2594
2595         setDefaultXhrHeader:function(b)
2596         {
2597             this.useDefaultXhrHeader = b;
2598         },
2599
2600         setPollingInterval:function(i)
2601         {
2602             if (typeof i == 'number' && isFinite(i)) {
2603                 this.pollInterval = i;
2604             }
2605         },
2606
2607         createXhrObject:function(transactionId)
2608         {
2609             var obj,http;
2610             try
2611             {
2612
2613                 http = new XMLHttpRequest();
2614
2615                 obj = { conn:http, tId:transactionId };
2616             }
2617             catch(e)
2618             {
2619                 for (var i = 0; i < this.activeX.length; ++i) {
2620                     try
2621                     {
2622
2623                         http = new ActiveXObject(this.activeX[i]);
2624
2625                         obj = { conn:http, tId:transactionId };
2626                         break;
2627                     }
2628                     catch(e) {
2629                     }
2630                 }
2631             }
2632             finally
2633             {
2634                 return obj;
2635             }
2636         },
2637
2638         getConnectionObject:function()
2639         {
2640             var o;
2641             var tId = this.transactionId;
2642
2643             try
2644             {
2645                 o = this.createXhrObject(tId);
2646                 if (o) {
2647                     this.transactionId++;
2648                 }
2649             }
2650             catch(e) {
2651             }
2652             finally
2653             {
2654                 return o;
2655             }
2656         },
2657
2658         asyncRequest:function(method, uri, callback, postData)
2659         {
2660             var o = this.getConnectionObject();
2661
2662             if (!o) {
2663                 return null;
2664             }
2665             else {
2666                 o.conn.open(method, uri, true);
2667
2668                 if (this.useDefaultXhrHeader) {
2669                     if (!this.defaultHeaders['X-Requested-With']) {
2670                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2671                     }
2672                 }
2673
2674                 if(postData && this.useDefaultHeader){
2675                     this.initHeader('Content-Type', this.defaultPostHeader);
2676                 }
2677
2678                  if (this.hasDefaultHeaders || this.hasHeaders) {
2679                     this.setHeader(o);
2680                 }
2681
2682                 this.handleReadyState(o, callback);
2683                 o.conn.send(postData || null);
2684
2685                 return o;
2686             }
2687         },
2688
2689         handleReadyState:function(o, callback)
2690         {
2691             var oConn = this;
2692
2693             if (callback && callback.timeout) {
2694                 this.timeout[o.tId] = window.setTimeout(function() {
2695                     oConn.abort(o, callback, true);
2696                 }, callback.timeout);
2697             }
2698
2699             this.poll[o.tId] = window.setInterval(
2700                     function() {
2701                         if (o.conn && o.conn.readyState == 4) {
2702                             window.clearInterval(oConn.poll[o.tId]);
2703                             delete oConn.poll[o.tId];
2704
2705                             if(callback && callback.timeout) {
2706                                 window.clearTimeout(oConn.timeout[o.tId]);
2707                                 delete oConn.timeout[o.tId];
2708                             }
2709
2710                             oConn.handleTransactionResponse(o, callback);
2711                         }
2712                     }
2713                     , this.pollInterval);
2714         },
2715
2716         handleTransactionResponse:function(o, callback, isAbort)
2717         {
2718
2719             if (!callback) {
2720                 this.releaseObject(o);
2721                 return;
2722             }
2723
2724             var httpStatus, responseObject;
2725
2726             try
2727             {
2728                 if (o.conn.status !== undefined && o.conn.status != 0) {
2729                     httpStatus = o.conn.status;
2730                 }
2731                 else {
2732                     httpStatus = 13030;
2733                 }
2734             }
2735             catch(e) {
2736
2737
2738                 httpStatus = 13030;
2739             }
2740
2741             if (httpStatus >= 200 && httpStatus < 300) {
2742                 responseObject = this.createResponseObject(o, callback.argument);
2743                 if (callback.success) {
2744                     if (!callback.scope) {
2745                         callback.success(responseObject);
2746                     }
2747                     else {
2748
2749
2750                         callback.success.apply(callback.scope, [responseObject]);
2751                     }
2752                 }
2753             }
2754             else {
2755                 switch (httpStatus) {
2756
2757                     case 12002:
2758                     case 12029:
2759                     case 12030:
2760                     case 12031:
2761                     case 12152:
2762                     case 13030:
2763                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2764                         if (callback.failure) {
2765                             if (!callback.scope) {
2766                                 callback.failure(responseObject);
2767                             }
2768                             else {
2769                                 callback.failure.apply(callback.scope, [responseObject]);
2770                             }
2771                         }
2772                         break;
2773                     default:
2774                         responseObject = this.createResponseObject(o, callback.argument);
2775                         if (callback.failure) {
2776                             if (!callback.scope) {
2777                                 callback.failure(responseObject);
2778                             }
2779                             else {
2780                                 callback.failure.apply(callback.scope, [responseObject]);
2781                             }
2782                         }
2783                 }
2784             }
2785
2786             this.releaseObject(o);
2787             responseObject = null;
2788         },
2789
2790         createResponseObject:function(o, callbackArg)
2791         {
2792             var obj = {};
2793             var headerObj = {};
2794
2795             try
2796             {
2797                 var headerStr = o.conn.getAllResponseHeaders();
2798                 var header = headerStr.split('\n');
2799                 for (var i = 0; i < header.length; i++) {
2800                     var delimitPos = header[i].indexOf(':');
2801                     if (delimitPos != -1) {
2802                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2803                     }
2804                 }
2805             }
2806             catch(e) {
2807             }
2808
2809             obj.tId = o.tId;
2810             obj.status = o.conn.status;
2811             obj.statusText = o.conn.statusText;
2812             obj.getResponseHeader = headerObj;
2813             obj.getAllResponseHeaders = headerStr;
2814             obj.responseText = o.conn.responseText;
2815             obj.responseXML = o.conn.responseXML;
2816
2817             if (typeof callbackArg !== undefined) {
2818                 obj.argument = callbackArg;
2819             }
2820
2821             return obj;
2822         },
2823
2824         createExceptionObject:function(tId, callbackArg, isAbort)
2825         {
2826             var COMM_CODE = 0;
2827             var COMM_ERROR = 'communication failure';
2828             var ABORT_CODE = -1;
2829             var ABORT_ERROR = 'transaction aborted';
2830
2831             var obj = {};
2832
2833             obj.tId = tId;
2834             if (isAbort) {
2835                 obj.status = ABORT_CODE;
2836                 obj.statusText = ABORT_ERROR;
2837             }
2838             else {
2839                 obj.status = COMM_CODE;
2840                 obj.statusText = COMM_ERROR;
2841             }
2842
2843             if (callbackArg) {
2844                 obj.argument = callbackArg;
2845             }
2846
2847             return obj;
2848         },
2849
2850         initHeader:function(label, value, isDefault)
2851         {
2852             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2853
2854             if (headerObj[label] === undefined) {
2855                 headerObj[label] = value;
2856             }
2857             else {
2858
2859
2860                 headerObj[label] = value + "," + headerObj[label];
2861             }
2862
2863             if (isDefault) {
2864                 this.hasDefaultHeaders = true;
2865             }
2866             else {
2867                 this.hasHeaders = true;
2868             }
2869         },
2870
2871
2872         setHeader:function(o)
2873         {
2874             if (this.hasDefaultHeaders) {
2875                 for (var prop in this.defaultHeaders) {
2876                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2877                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2878                     }
2879                 }
2880             }
2881
2882             if (this.hasHeaders) {
2883                 for (var prop in this.headers) {
2884                     if (this.headers.hasOwnProperty(prop)) {
2885                         o.conn.setRequestHeader(prop, this.headers[prop]);
2886                     }
2887                 }
2888                 this.headers = {};
2889                 this.hasHeaders = false;
2890             }
2891         },
2892
2893         resetDefaultHeaders:function() {
2894             delete this.defaultHeaders;
2895             this.defaultHeaders = {};
2896             this.hasDefaultHeaders = false;
2897         },
2898
2899         abort:function(o, callback, isTimeout)
2900         {
2901             if(this.isCallInProgress(o)) {
2902                 o.conn.abort();
2903                 window.clearInterval(this.poll[o.tId]);
2904                 delete this.poll[o.tId];
2905                 if (isTimeout) {
2906                     delete this.timeout[o.tId];
2907                 }
2908
2909                 this.handleTransactionResponse(o, callback, true);
2910
2911                 return true;
2912             }
2913             else {
2914                 return false;
2915             }
2916         },
2917
2918
2919         isCallInProgress:function(o)
2920         {
2921             if (o && o.conn) {
2922                 return o.conn.readyState != 4 && o.conn.readyState != 0;
2923             }
2924             else {
2925
2926                 return false;
2927             }
2928         },
2929
2930
2931         releaseObject:function(o)
2932         {
2933
2934             o.conn = null;
2935
2936             o = null;
2937         },
2938
2939         activeX:[
2940         'MSXML2.XMLHTTP.3.0',
2941         'MSXML2.XMLHTTP',
2942         'Microsoft.XMLHTTP'
2943         ]
2944
2945
2946     };
2947 })();/*
2948  * Portions of this file are based on pieces of Yahoo User Interface Library
2949  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2950  * YUI licensed under the BSD License:
2951  * http://developer.yahoo.net/yui/license.txt
2952  * <script type="text/javascript">
2953  *
2954  */
2955
2956 Roo.lib.Region = function(t, r, b, l) {
2957     this.top = t;
2958     this[1] = t;
2959     this.right = r;
2960     this.bottom = b;
2961     this.left = l;
2962     this[0] = l;
2963 };
2964
2965
2966 Roo.lib.Region.prototype = {
2967     contains : function(region) {
2968         return ( region.left >= this.left &&
2969                  region.right <= this.right &&
2970                  region.top >= this.top &&
2971                  region.bottom <= this.bottom    );
2972
2973     },
2974
2975     getArea : function() {
2976         return ( (this.bottom - this.top) * (this.right - this.left) );
2977     },
2978
2979     intersect : function(region) {
2980         var t = Math.max(this.top, region.top);
2981         var r = Math.min(this.right, region.right);
2982         var b = Math.min(this.bottom, region.bottom);
2983         var l = Math.max(this.left, region.left);
2984
2985         if (b >= t && r >= l) {
2986             return new Roo.lib.Region(t, r, b, l);
2987         } else {
2988             return null;
2989         }
2990     },
2991     union : function(region) {
2992         var t = Math.min(this.top, region.top);
2993         var r = Math.max(this.right, region.right);
2994         var b = Math.max(this.bottom, region.bottom);
2995         var l = Math.min(this.left, region.left);
2996
2997         return new Roo.lib.Region(t, r, b, l);
2998     },
2999
3000     adjust : function(t, l, b, r) {
3001         this.top += t;
3002         this.left += l;
3003         this.right += r;
3004         this.bottom += b;
3005         return this;
3006     }
3007 };
3008
3009 Roo.lib.Region.getRegion = function(el) {
3010     var p = Roo.lib.Dom.getXY(el);
3011
3012     var t = p[1];
3013     var r = p[0] + el.offsetWidth;
3014     var b = p[1] + el.offsetHeight;
3015     var l = p[0];
3016
3017     return new Roo.lib.Region(t, r, b, l);
3018 };
3019 /*
3020  * Portions of this file are based on pieces of Yahoo User Interface Library
3021  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3022  * YUI licensed under the BSD License:
3023  * http://developer.yahoo.net/yui/license.txt
3024  * <script type="text/javascript">
3025  *
3026  */
3027 //@@dep Roo.lib.Region
3028
3029
3030 Roo.lib.Point = function(x, y) {
3031     if (x instanceof Array) {
3032         y = x[1];
3033         x = x[0];
3034     }
3035     this.x = this.right = this.left = this[0] = x;
3036     this.y = this.top = this.bottom = this[1] = y;
3037 };
3038
3039 Roo.lib.Point.prototype = new Roo.lib.Region();
3040 /*
3041  * Portions of this file are based on pieces of Yahoo User Interface Library
3042  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3043  * YUI licensed under the BSD License:
3044  * http://developer.yahoo.net/yui/license.txt
3045  * <script type="text/javascript">
3046  *
3047  */
3048  
3049 (function() {   
3050
3051     Roo.lib.Anim = {
3052         scroll : function(el, args, duration, easing, cb, scope) {
3053             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3054         },
3055
3056         motion : function(el, args, duration, easing, cb, scope) {
3057             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3058         },
3059
3060         color : function(el, args, duration, easing, cb, scope) {
3061             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3062         },
3063
3064         run : function(el, args, duration, easing, cb, scope, type) {
3065             type = type || Roo.lib.AnimBase;
3066             if (typeof easing == "string") {
3067                 easing = Roo.lib.Easing[easing];
3068             }
3069             var anim = new type(el, args, duration, easing);
3070             anim.animateX(function() {
3071                 Roo.callback(cb, scope);
3072             });
3073             return anim;
3074         }
3075     };
3076 })();/*
3077  * Portions of this file are based on pieces of Yahoo User Interface Library
3078  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3079  * YUI licensed under the BSD License:
3080  * http://developer.yahoo.net/yui/license.txt
3081  * <script type="text/javascript">
3082  *
3083  */
3084
3085 (function() {    
3086     var libFlyweight;
3087     
3088     function fly(el) {
3089         if (!libFlyweight) {
3090             libFlyweight = new Roo.Element.Flyweight();
3091         }
3092         libFlyweight.dom = el;
3093         return libFlyweight;
3094     }
3095
3096     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3097     
3098    
3099     
3100     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3101         if (el) {
3102             this.init(el, attributes, duration, method);
3103         }
3104     };
3105
3106     Roo.lib.AnimBase.fly = fly;
3107     
3108     
3109     
3110     Roo.lib.AnimBase.prototype = {
3111
3112         toString: function() {
3113             var el = this.getEl();
3114             var id = el.id || el.tagName;
3115             return ("Anim " + id);
3116         },
3117
3118         patterns: {
3119             noNegatives:        /width|height|opacity|padding/i,
3120             offsetAttribute:  /^((width|height)|(top|left))$/,
3121             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3122             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3123         },
3124
3125
3126         doMethod: function(attr, start, end) {
3127             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3128         },
3129
3130
3131         setAttribute: function(attr, val, unit) {
3132             if (this.patterns.noNegatives.test(attr)) {
3133                 val = (val > 0) ? val : 0;
3134             }
3135
3136             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3137         },
3138
3139
3140         getAttribute: function(attr) {
3141             var el = this.getEl();
3142             var val = fly(el).getStyle(attr);
3143
3144             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3145                 return parseFloat(val);
3146             }
3147
3148             var a = this.patterns.offsetAttribute.exec(attr) || [];
3149             var pos = !!( a[3] );
3150             var box = !!( a[2] );
3151
3152
3153             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3154                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3155             } else {
3156                 val = 0;
3157             }
3158
3159             return val;
3160         },
3161
3162
3163         getDefaultUnit: function(attr) {
3164             if (this.patterns.defaultUnit.test(attr)) {
3165                 return 'px';
3166             }
3167
3168             return '';
3169         },
3170
3171         animateX : function(callback, scope) {
3172             var f = function() {
3173                 this.onComplete.removeListener(f);
3174                 if (typeof callback == "function") {
3175                     callback.call(scope || this, this);
3176                 }
3177             };
3178             this.onComplete.addListener(f, this);
3179             this.animate();
3180         },
3181
3182
3183         setRuntimeAttribute: function(attr) {
3184             var start;
3185             var end;
3186             var attributes = this.attributes;
3187
3188             this.runtimeAttributes[attr] = {};
3189
3190             var isset = function(prop) {
3191                 return (typeof prop !== 'undefined');
3192             };
3193
3194             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3195                 return false;
3196             }
3197
3198             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3199
3200
3201             if (isset(attributes[attr]['to'])) {
3202                 end = attributes[attr]['to'];
3203             } else if (isset(attributes[attr]['by'])) {
3204                 if (start.constructor == Array) {
3205                     end = [];
3206                     for (var i = 0, len = start.length; i < len; ++i) {
3207                         end[i] = start[i] + attributes[attr]['by'][i];
3208                     }
3209                 } else {
3210                     end = start + attributes[attr]['by'];
3211                 }
3212             }
3213
3214             this.runtimeAttributes[attr].start = start;
3215             this.runtimeAttributes[attr].end = end;
3216
3217
3218             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3219         },
3220
3221
3222         init: function(el, attributes, duration, method) {
3223
3224             var isAnimated = false;
3225
3226
3227             var startTime = null;
3228
3229
3230             var actualFrames = 0;
3231
3232
3233             el = Roo.getDom(el);
3234
3235
3236             this.attributes = attributes || {};
3237
3238
3239             this.duration = duration || 1;
3240
3241
3242             this.method = method || Roo.lib.Easing.easeNone;
3243
3244
3245             this.useSeconds = true;
3246
3247
3248             this.currentFrame = 0;
3249
3250
3251             this.totalFrames = Roo.lib.AnimMgr.fps;
3252
3253
3254             this.getEl = function() {
3255                 return el;
3256             };
3257
3258
3259             this.isAnimated = function() {
3260                 return isAnimated;
3261             };
3262
3263
3264             this.getStartTime = function() {
3265                 return startTime;
3266             };
3267
3268             this.runtimeAttributes = {};
3269
3270
3271             this.animate = function() {
3272                 if (this.isAnimated()) {
3273                     return false;
3274                 }
3275
3276                 this.currentFrame = 0;
3277
3278                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3279
3280                 Roo.lib.AnimMgr.registerElement(this);
3281             };
3282
3283
3284             this.stop = function(finish) {
3285                 if (finish) {
3286                     this.currentFrame = this.totalFrames;
3287                     this._onTween.fire();
3288                 }
3289                 Roo.lib.AnimMgr.stop(this);
3290             };
3291
3292             var onStart = function() {
3293                 this.onStart.fire();
3294
3295                 this.runtimeAttributes = {};
3296                 for (var attr in this.attributes) {
3297                     this.setRuntimeAttribute(attr);
3298                 }
3299
3300                 isAnimated = true;
3301                 actualFrames = 0;
3302                 startTime = new Date();
3303             };
3304
3305
3306             var onTween = function() {
3307                 var data = {
3308                     duration: new Date() - this.getStartTime(),
3309                     currentFrame: this.currentFrame
3310                 };
3311
3312                 data.toString = function() {
3313                     return (
3314                             'duration: ' + data.duration +
3315                             ', currentFrame: ' + data.currentFrame
3316                             );
3317                 };
3318
3319                 this.onTween.fire(data);
3320
3321                 var runtimeAttributes = this.runtimeAttributes;
3322
3323                 for (var attr in runtimeAttributes) {
3324                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3325                 }
3326
3327                 actualFrames += 1;
3328             };
3329
3330             var onComplete = function() {
3331                 var actual_duration = (new Date() - startTime) / 1000 ;
3332
3333                 var data = {
3334                     duration: actual_duration,
3335                     frames: actualFrames,
3336                     fps: actualFrames / actual_duration
3337                 };
3338
3339                 data.toString = function() {
3340                     return (
3341                             'duration: ' + data.duration +
3342                             ', frames: ' + data.frames +
3343                             ', fps: ' + data.fps
3344                             );
3345                 };
3346
3347                 isAnimated = false;
3348                 actualFrames = 0;
3349                 this.onComplete.fire(data);
3350             };
3351
3352
3353             this._onStart = new Roo.util.Event(this);
3354             this.onStart = new Roo.util.Event(this);
3355             this.onTween = new Roo.util.Event(this);
3356             this._onTween = new Roo.util.Event(this);
3357             this.onComplete = new Roo.util.Event(this);
3358             this._onComplete = new Roo.util.Event(this);
3359             this._onStart.addListener(onStart);
3360             this._onTween.addListener(onTween);
3361             this._onComplete.addListener(onComplete);
3362         }
3363     };
3364 })();
3365 /*
3366  * Portions of this file are based on pieces of Yahoo User Interface Library
3367  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3368  * YUI licensed under the BSD License:
3369  * http://developer.yahoo.net/yui/license.txt
3370  * <script type="text/javascript">
3371  *
3372  */
3373
3374 Roo.lib.AnimMgr = new function() {
3375
3376         var thread = null;
3377
3378
3379         var queue = [];
3380
3381
3382         var tweenCount = 0;
3383
3384
3385         this.fps = 1000;
3386
3387
3388         this.delay = 1;
3389
3390
3391         this.registerElement = function(tween) {
3392             queue[queue.length] = tween;
3393             tweenCount += 1;
3394             tween._onStart.fire();
3395             this.start();
3396         };
3397
3398
3399         this.unRegister = function(tween, index) {
3400             tween._onComplete.fire();
3401             index = index || getIndex(tween);
3402             if (index != -1) {
3403                 queue.splice(index, 1);
3404             }
3405
3406             tweenCount -= 1;
3407             if (tweenCount <= 0) {
3408                 this.stop();
3409             }
3410         };
3411
3412
3413         this.start = function() {
3414             if (thread === null) {
3415                 thread = setInterval(this.run, this.delay);
3416             }
3417         };
3418
3419
3420         this.stop = function(tween) {
3421             if (!tween) {
3422                 clearInterval(thread);
3423
3424                 for (var i = 0, len = queue.length; i < len; ++i) {
3425                     if (queue[0].isAnimated()) {
3426                         this.unRegister(queue[0], 0);
3427                     }
3428                 }
3429
3430                 queue = [];
3431                 thread = null;
3432                 tweenCount = 0;
3433             }
3434             else {
3435                 this.unRegister(tween);
3436             }
3437         };
3438
3439
3440         this.run = function() {
3441             for (var i = 0, len = queue.length; i < len; ++i) {
3442                 var tween = queue[i];
3443                 if (!tween || !tween.isAnimated()) {
3444                     continue;
3445                 }
3446
3447                 if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3448                 {
3449                     tween.currentFrame += 1;
3450
3451                     if (tween.useSeconds) {
3452                         correctFrame(tween);
3453                     }
3454                     tween._onTween.fire();
3455                 }
3456                 else {
3457                     Roo.lib.AnimMgr.stop(tween, i);
3458                 }
3459             }
3460         };
3461
3462         var getIndex = function(anim) {
3463             for (var i = 0, len = queue.length; i < len; ++i) {
3464                 if (queue[i] == anim) {
3465                     return i;
3466                 }
3467             }
3468             return -1;
3469         };
3470
3471
3472         var correctFrame = function(tween) {
3473             var frames = tween.totalFrames;
3474             var frame = tween.currentFrame;
3475             var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3476             var elapsed = (new Date() - tween.getStartTime());
3477             var tweak = 0;
3478
3479             if (elapsed < tween.duration * 1000) {
3480                 tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3481             } else {
3482                 tweak = frames - (frame + 1);
3483             }
3484             if (tweak > 0 && isFinite(tweak)) {
3485                 if (tween.currentFrame + tweak >= frames) {
3486                     tweak = frames - (frame + 1);
3487                 }
3488
3489                 tween.currentFrame += tweak;
3490             }
3491         };
3492     };/*
3493  * Portions of this file are based on pieces of Yahoo User Interface Library
3494  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3495  * YUI licensed under the BSD License:
3496  * http://developer.yahoo.net/yui/license.txt
3497  * <script type="text/javascript">
3498  *
3499  */
3500 Roo.lib.Bezier = new function() {
3501
3502         this.getPosition = function(points, t) {
3503             var n = points.length;
3504             var tmp = [];
3505
3506             for (var i = 0; i < n; ++i) {
3507                 tmp[i] = [points[i][0], points[i][1]];
3508             }
3509
3510             for (var j = 1; j < n; ++j) {
3511                 for (i = 0; i < n - j; ++i) {
3512                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3513                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3514                 }
3515             }
3516
3517             return [ tmp[0][0], tmp[0][1] ];
3518
3519         };
3520     };/*
3521  * Portions of this file are based on pieces of Yahoo User Interface Library
3522  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3523  * YUI licensed under the BSD License:
3524  * http://developer.yahoo.net/yui/license.txt
3525  * <script type="text/javascript">
3526  *
3527  */
3528 (function() {
3529
3530     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3531         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3532     };
3533
3534     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3535
3536     var fly = Roo.lib.AnimBase.fly;
3537     var Y = Roo.lib;
3538     var superclass = Y.ColorAnim.superclass;
3539     var proto = Y.ColorAnim.prototype;
3540
3541     proto.toString = function() {
3542         var el = this.getEl();
3543         var id = el.id || el.tagName;
3544         return ("ColorAnim " + id);
3545     };
3546
3547     proto.patterns.color = /color$/i;
3548     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3549     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3550     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3551     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3552
3553
3554     proto.parseColor = function(s) {
3555         if (s.length == 3) {
3556             return s;
3557         }
3558
3559         var c = this.patterns.hex.exec(s);
3560         if (c && c.length == 4) {
3561             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3562         }
3563
3564         c = this.patterns.rgb.exec(s);
3565         if (c && c.length == 4) {
3566             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3567         }
3568
3569         c = this.patterns.hex3.exec(s);
3570         if (c && c.length == 4) {
3571             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3572         }
3573
3574         return null;
3575     };
3576     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3577     proto.getAttribute = function(attr) {
3578         var el = this.getEl();
3579         if (this.patterns.color.test(attr)) {
3580             var val = fly(el).getStyle(attr);
3581
3582             if (this.patterns.transparent.test(val)) {
3583                 var parent = el.parentNode;
3584                 val = fly(parent).getStyle(attr);
3585
3586                 while (parent && this.patterns.transparent.test(val)) {
3587                     parent = parent.parentNode;
3588                     val = fly(parent).getStyle(attr);
3589                     if (parent.tagName.toUpperCase() == 'HTML') {
3590                         val = '#fff';
3591                     }
3592                 }
3593             }
3594         } else {
3595             val = superclass.getAttribute.call(this, attr);
3596         }
3597
3598         return val;
3599     };
3600     proto.getAttribute = function(attr) {
3601         var el = this.getEl();
3602         if (this.patterns.color.test(attr)) {
3603             var val = fly(el).getStyle(attr);
3604
3605             if (this.patterns.transparent.test(val)) {
3606                 var parent = el.parentNode;
3607                 val = fly(parent).getStyle(attr);
3608
3609                 while (parent && this.patterns.transparent.test(val)) {
3610                     parent = parent.parentNode;
3611                     val = fly(parent).getStyle(attr);
3612                     if (parent.tagName.toUpperCase() == 'HTML') {
3613                         val = '#fff';
3614                     }
3615                 }
3616             }
3617         } else {
3618             val = superclass.getAttribute.call(this, attr);
3619         }
3620
3621         return val;
3622     };
3623
3624     proto.doMethod = function(attr, start, end) {
3625         var val;
3626
3627         if (this.patterns.color.test(attr)) {
3628             val = [];
3629             for (var i = 0, len = start.length; i < len; ++i) {
3630                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3631             }
3632
3633             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3634         }
3635         else {
3636             val = superclass.doMethod.call(this, attr, start, end);
3637         }
3638
3639         return val;
3640     };
3641
3642     proto.setRuntimeAttribute = function(attr) {
3643         superclass.setRuntimeAttribute.call(this, attr);
3644
3645         if (this.patterns.color.test(attr)) {
3646             var attributes = this.attributes;
3647             var start = this.parseColor(this.runtimeAttributes[attr].start);
3648             var end = this.parseColor(this.runtimeAttributes[attr].end);
3649
3650             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3651                 end = this.parseColor(attributes[attr].by);
3652
3653                 for (var i = 0, len = start.length; i < len; ++i) {
3654                     end[i] = start[i] + end[i];
3655                 }
3656             }
3657
3658             this.runtimeAttributes[attr].start = start;
3659             this.runtimeAttributes[attr].end = end;
3660         }
3661     };
3662 })();
3663
3664 /*
3665  * Portions of this file are based on pieces of Yahoo User Interface Library
3666  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3667  * YUI licensed under the BSD License:
3668  * http://developer.yahoo.net/yui/license.txt
3669  * <script type="text/javascript">
3670  *
3671  */
3672 Roo.lib.Easing = {
3673
3674
3675     easeNone: function (t, b, c, d) {
3676         return c * t / d + b;
3677     },
3678
3679
3680     easeIn: function (t, b, c, d) {
3681         return c * (t /= d) * t + b;
3682     },
3683
3684
3685     easeOut: function (t, b, c, d) {
3686         return -c * (t /= d) * (t - 2) + b;
3687     },
3688
3689
3690     easeBoth: function (t, b, c, d) {
3691         if ((t /= d / 2) < 1) {
3692             return c / 2 * t * t + b;
3693         }
3694
3695         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3696     },
3697
3698
3699     easeInStrong: function (t, b, c, d) {
3700         return c * (t /= d) * t * t * t + b;
3701     },
3702
3703
3704     easeOutStrong: function (t, b, c, d) {
3705         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3706     },
3707
3708
3709     easeBothStrong: function (t, b, c, d) {
3710         if ((t /= d / 2) < 1) {
3711             return c / 2 * t * t * t * t + b;
3712         }
3713
3714         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3715     },
3716
3717
3718
3719     elasticIn: function (t, b, c, d, a, p) {
3720         if (t == 0) {
3721             return b;
3722         }
3723         if ((t /= d) == 1) {
3724             return b + c;
3725         }
3726         if (!p) {
3727             p = d * .3;
3728         }
3729
3730         if (!a || a < Math.abs(c)) {
3731             a = c;
3732             var s = p / 4;
3733         }
3734         else {
3735             var s = p / (2 * Math.PI) * Math.asin(c / a);
3736         }
3737
3738         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3739     },
3740
3741
3742     elasticOut: function (t, b, c, d, a, p) {
3743         if (t == 0) {
3744             return b;
3745         }
3746         if ((t /= d) == 1) {
3747             return b + c;
3748         }
3749         if (!p) {
3750             p = d * .3;
3751         }
3752
3753         if (!a || a < Math.abs(c)) {
3754             a = c;
3755             var s = p / 4;
3756         }
3757         else {
3758             var s = p / (2 * Math.PI) * Math.asin(c / a);
3759         }
3760
3761         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3762     },
3763
3764
3765     elasticBoth: function (t, b, c, d, a, p) {
3766         if (t == 0) {
3767             return b;
3768         }
3769
3770         if ((t /= d / 2) == 2) {
3771             return b + c;
3772         }
3773
3774         if (!p) {
3775             p = d * (.3 * 1.5);
3776         }
3777
3778         if (!a || a < Math.abs(c)) {
3779             a = c;
3780             var s = p / 4;
3781         }
3782         else {
3783             var s = p / (2 * Math.PI) * Math.asin(c / a);
3784         }
3785
3786         if (t < 1) {
3787             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3788                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3789         }
3790         return a * Math.pow(2, -10 * (t -= 1)) *
3791                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3792     },
3793
3794
3795
3796     backIn: function (t, b, c, d, s) {
3797         if (typeof s == 'undefined') {
3798             s = 1.70158;
3799         }
3800         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3801     },
3802
3803
3804     backOut: function (t, b, c, d, s) {
3805         if (typeof s == 'undefined') {
3806             s = 1.70158;
3807         }
3808         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3809     },
3810
3811
3812     backBoth: function (t, b, c, d, s) {
3813         if (typeof s == 'undefined') {
3814             s = 1.70158;
3815         }
3816
3817         if ((t /= d / 2 ) < 1) {
3818             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3819         }
3820         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3821     },
3822
3823
3824     bounceIn: function (t, b, c, d) {
3825         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3826     },
3827
3828
3829     bounceOut: function (t, b, c, d) {
3830         if ((t /= d) < (1 / 2.75)) {
3831             return c * (7.5625 * t * t) + b;
3832         } else if (t < (2 / 2.75)) {
3833             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3834         } else if (t < (2.5 / 2.75)) {
3835             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3836         }
3837         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3838     },
3839
3840
3841     bounceBoth: function (t, b, c, d) {
3842         if (t < d / 2) {
3843             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3844         }
3845         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3846     }
3847 };/*
3848  * Portions of this file are based on pieces of Yahoo User Interface Library
3849  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3850  * YUI licensed under the BSD License:
3851  * http://developer.yahoo.net/yui/license.txt
3852  * <script type="text/javascript">
3853  *
3854  */
3855     (function() {
3856         Roo.lib.Motion = function(el, attributes, duration, method) {
3857             if (el) {
3858                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3859             }
3860         };
3861
3862         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3863
3864
3865         var Y = Roo.lib;
3866         var superclass = Y.Motion.superclass;
3867         var proto = Y.Motion.prototype;
3868
3869         proto.toString = function() {
3870             var el = this.getEl();
3871             var id = el.id || el.tagName;
3872             return ("Motion " + id);
3873         };
3874
3875         proto.patterns.points = /^points$/i;
3876
3877         proto.setAttribute = function(attr, val, unit) {
3878             if (this.patterns.points.test(attr)) {
3879                 unit = unit || 'px';
3880                 superclass.setAttribute.call(this, 'left', val[0], unit);
3881                 superclass.setAttribute.call(this, 'top', val[1], unit);
3882             } else {
3883                 superclass.setAttribute.call(this, attr, val, unit);
3884             }
3885         };
3886
3887         proto.getAttribute = function(attr) {
3888             if (this.patterns.points.test(attr)) {
3889                 var val = [
3890                         superclass.getAttribute.call(this, 'left'),
3891                         superclass.getAttribute.call(this, 'top')
3892                         ];
3893             } else {
3894                 val = superclass.getAttribute.call(this, attr);
3895             }
3896
3897             return val;
3898         };
3899
3900         proto.doMethod = function(attr, start, end) {
3901             var val = null;
3902
3903             if (this.patterns.points.test(attr)) {
3904                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
3905                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
3906             } else {
3907                 val = superclass.doMethod.call(this, attr, start, end);
3908             }
3909             return val;
3910         };
3911
3912         proto.setRuntimeAttribute = function(attr) {
3913             if (this.patterns.points.test(attr)) {
3914                 var el = this.getEl();
3915                 var attributes = this.attributes;
3916                 var start;
3917                 var control = attributes['points']['control'] || [];
3918                 var end;
3919                 var i, len;
3920
3921                 if (control.length > 0 && !(control[0] instanceof Array)) {
3922                     control = [control];
3923                 } else {
3924                     var tmp = [];
3925                     for (i = 0,len = control.length; i < len; ++i) {
3926                         tmp[i] = control[i];
3927                     }
3928                     control = tmp;
3929                 }
3930
3931                 Roo.fly(el).position();
3932
3933                 if (isset(attributes['points']['from'])) {
3934                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
3935                 }
3936                 else {
3937                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
3938                 }
3939
3940                 start = this.getAttribute('points');
3941
3942
3943                 if (isset(attributes['points']['to'])) {
3944                     end = translateValues.call(this, attributes['points']['to'], start);
3945
3946                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
3947                     for (i = 0,len = control.length; i < len; ++i) {
3948                         control[i] = translateValues.call(this, control[i], start);
3949                     }
3950
3951
3952                 } else if (isset(attributes['points']['by'])) {
3953                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
3954
3955                     for (i = 0,len = control.length; i < len; ++i) {
3956                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
3957                     }
3958                 }
3959
3960                 this.runtimeAttributes[attr] = [start];
3961
3962                 if (control.length > 0) {
3963                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
3964                 }
3965
3966                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
3967             }
3968             else {
3969                 superclass.setRuntimeAttribute.call(this, attr);
3970             }
3971         };
3972
3973         var translateValues = function(val, start) {
3974             var pageXY = Roo.lib.Dom.getXY(this.getEl());
3975             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
3976
3977             return val;
3978         };
3979
3980         var isset = function(prop) {
3981             return (typeof prop !== 'undefined');
3982         };
3983     })();
3984 /*
3985  * Portions of this file are based on pieces of Yahoo User Interface Library
3986  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3987  * YUI licensed under the BSD License:
3988  * http://developer.yahoo.net/yui/license.txt
3989  * <script type="text/javascript">
3990  *
3991  */
3992     (function() {
3993         Roo.lib.Scroll = function(el, attributes, duration, method) {
3994             if (el) {
3995                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
3996             }
3997         };
3998
3999         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4000
4001
4002         var Y = Roo.lib;
4003         var superclass = Y.Scroll.superclass;
4004         var proto = Y.Scroll.prototype;
4005
4006         proto.toString = function() {
4007             var el = this.getEl();
4008             var id = el.id || el.tagName;
4009             return ("Scroll " + id);
4010         };
4011
4012         proto.doMethod = function(attr, start, end) {
4013             var val = null;
4014
4015             if (attr == 'scroll') {
4016                 val = [
4017                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4018                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4019                         ];
4020
4021             } else {
4022                 val = superclass.doMethod.call(this, attr, start, end);
4023             }
4024             return val;
4025         };
4026
4027         proto.getAttribute = function(attr) {
4028             var val = null;
4029             var el = this.getEl();
4030
4031             if (attr == 'scroll') {
4032                 val = [ el.scrollLeft, el.scrollTop ];
4033             } else {
4034                 val = superclass.getAttribute.call(this, attr);
4035             }
4036
4037             return val;
4038         };
4039
4040         proto.setAttribute = function(attr, val, unit) {
4041             var el = this.getEl();
4042
4043             if (attr == 'scroll') {
4044                 el.scrollLeft = val[0];
4045                 el.scrollTop = val[1];
4046             } else {
4047                 superclass.setAttribute.call(this, attr, val, unit);
4048             }
4049         };
4050     })();
4051 /*
4052  * Based on:
4053  * Ext JS Library 1.1.1
4054  * Copyright(c) 2006-2007, Ext JS, LLC.
4055  *
4056  * Originally Released Under LGPL - original licence link has changed is not relivant.
4057  *
4058  * Fork - LGPL
4059  * <script type="text/javascript">
4060  */
4061
4062
4063 // nasty IE9 hack - what a pile of crap that is..
4064
4065  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4066     Range.prototype.createContextualFragment = function (html) {
4067         var doc = window.document;
4068         var container = doc.createElement("div");
4069         container.innerHTML = html;
4070         var frag = doc.createDocumentFragment(), n;
4071         while ((n = container.firstChild)) {
4072             frag.appendChild(n);
4073         }
4074         return frag;
4075     };
4076 }
4077
4078 /**
4079  * @class Roo.DomHelper
4080  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4081  * For more information see <a href="http://www.jackslocum.com/yui/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">this blog post with examples</a>.
4082  * @singleton
4083  */
4084 Roo.DomHelper = function(){
4085     var tempTableEl = null;
4086     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4087     var tableRe = /^table|tbody|tr|td$/i;
4088     var xmlns = {};
4089     // build as innerHTML where available
4090     /** @ignore */
4091     var createHtml = function(o){
4092         if(typeof o == 'string'){
4093             return o;
4094         }
4095         var b = "";
4096         if(!o.tag){
4097             o.tag = "div";
4098         }
4099         b += "<" + o.tag;
4100         for(var attr in o){
4101             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") continue;
4102             if(attr == "style"){
4103                 var s = o["style"];
4104                 if(typeof s == "function"){
4105                     s = s.call();
4106                 }
4107                 if(typeof s == "string"){
4108                     b += ' style="' + s + '"';
4109                 }else if(typeof s == "object"){
4110                     b += ' style="';
4111                     for(var key in s){
4112                         if(typeof s[key] != "function"){
4113                             b += key + ":" + s[key] + ";";
4114                         }
4115                     }
4116                     b += '"';
4117                 }
4118             }else{
4119                 if(attr == "cls"){
4120                     b += ' class="' + o["cls"] + '"';
4121                 }else if(attr == "htmlFor"){
4122                     b += ' for="' + o["htmlFor"] + '"';
4123                 }else{
4124                     b += " " + attr + '="' + o[attr] + '"';
4125                 }
4126             }
4127         }
4128         if(emptyTags.test(o.tag)){
4129             b += "/>";
4130         }else{
4131             b += ">";
4132             var cn = o.children || o.cn;
4133             if(cn){
4134                 //http://bugs.kde.org/show_bug.cgi?id=71506
4135                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4136                     for(var i = 0, len = cn.length; i < len; i++) {
4137                         b += createHtml(cn[i], b);
4138                     }
4139                 }else{
4140                     b += createHtml(cn, b);
4141                 }
4142             }
4143             if(o.html){
4144                 b += o.html;
4145             }
4146             b += "</" + o.tag + ">";
4147         }
4148         return b;
4149     };
4150
4151     // build as dom
4152     /** @ignore */
4153     var createDom = function(o, parentNode){
4154          
4155         // defininition craeted..
4156         var ns = false;
4157         if (o.ns && o.ns != 'html') {
4158                
4159             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4160                 xmlns[o.ns] = o.xmlns;
4161                 ns = o.xmlns;
4162             }
4163             if (typeof(xmlns[o.ns]) == 'undefined') {
4164                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4165             }
4166             ns = xmlns[o.ns];
4167         }
4168         
4169         
4170         if (typeof(o) == 'string') {
4171             return parentNode.appendChild(document.createTextNode(o));
4172         }
4173         o.tag = o.tag || div;
4174         if (o.ns && Roo.isIE) {
4175             ns = false;
4176             o.tag = o.ns + ':' + o.tag;
4177             
4178         }
4179         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4180         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4181         for(var attr in o){
4182             
4183             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4184                     attr == "style" || typeof o[attr] == "function") continue;
4185                     
4186             if(attr=="cls" && Roo.isIE){
4187                 el.className = o["cls"];
4188             }else{
4189                 if(useSet) el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);
4190                 else el[attr] = o[attr];
4191             }
4192         }
4193         Roo.DomHelper.applyStyles(el, o.style);
4194         var cn = o.children || o.cn;
4195         if(cn){
4196             //http://bugs.kde.org/show_bug.cgi?id=71506
4197              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4198                 for(var i = 0, len = cn.length; i < len; i++) {
4199                     createDom(cn[i], el);
4200                 }
4201             }else{
4202                 createDom(cn, el);
4203             }
4204         }
4205         if(o.html){
4206             el.innerHTML = o.html;
4207         }
4208         if(parentNode){
4209            parentNode.appendChild(el);
4210         }
4211         return el;
4212     };
4213
4214     var ieTable = function(depth, s, h, e){
4215         tempTableEl.innerHTML = [s, h, e].join('');
4216         var i = -1, el = tempTableEl;
4217         while(++i < depth){
4218             el = el.firstChild;
4219         }
4220         return el;
4221     };
4222
4223     // kill repeat to save bytes
4224     var ts = '<table>',
4225         te = '</table>',
4226         tbs = ts+'<tbody>',
4227         tbe = '</tbody>'+te,
4228         trs = tbs + '<tr>',
4229         tre = '</tr>'+tbe;
4230
4231     /**
4232      * @ignore
4233      * Nasty code for IE's broken table implementation
4234      */
4235     var insertIntoTable = function(tag, where, el, html){
4236         if(!tempTableEl){
4237             tempTableEl = document.createElement('div');
4238         }
4239         var node;
4240         var before = null;
4241         if(tag == 'td'){
4242             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4243                 return;
4244             }
4245             if(where == 'beforebegin'){
4246                 before = el;
4247                 el = el.parentNode;
4248             } else{
4249                 before = el.nextSibling;
4250                 el = el.parentNode;
4251             }
4252             node = ieTable(4, trs, html, tre);
4253         }
4254         else if(tag == 'tr'){
4255             if(where == 'beforebegin'){
4256                 before = el;
4257                 el = el.parentNode;
4258                 node = ieTable(3, tbs, html, tbe);
4259             } else if(where == 'afterend'){
4260                 before = el.nextSibling;
4261                 el = el.parentNode;
4262                 node = ieTable(3, tbs, html, tbe);
4263             } else{ // INTO a TR
4264                 if(where == 'afterbegin'){
4265                     before = el.firstChild;
4266                 }
4267                 node = ieTable(4, trs, html, tre);
4268             }
4269         } else if(tag == 'tbody'){
4270             if(where == 'beforebegin'){
4271                 before = el;
4272                 el = el.parentNode;
4273                 node = ieTable(2, ts, html, te);
4274             } else if(where == 'afterend'){
4275                 before = el.nextSibling;
4276                 el = el.parentNode;
4277                 node = ieTable(2, ts, html, te);
4278             } else{
4279                 if(where == 'afterbegin'){
4280                     before = el.firstChild;
4281                 }
4282                 node = ieTable(3, tbs, html, tbe);
4283             }
4284         } else{ // TABLE
4285             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4286                 return;
4287             }
4288             if(where == 'afterbegin'){
4289                 before = el.firstChild;
4290             }
4291             node = ieTable(2, ts, html, te);
4292         }
4293         el.insertBefore(node, before);
4294         return node;
4295     };
4296
4297     return {
4298     /** True to force the use of DOM instead of html fragments @type Boolean */
4299     useDom : false,
4300
4301     /**
4302      * Returns the markup for the passed Element(s) config
4303      * @param {Object} o The Dom object spec (and children)
4304      * @return {String}
4305      */
4306     markup : function(o){
4307         return createHtml(o);
4308     },
4309
4310     /**
4311      * Applies a style specification to an element
4312      * @param {String/HTMLElement} el The element to apply styles to
4313      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4314      * a function which returns such a specification.
4315      */
4316     applyStyles : function(el, styles){
4317         if(styles){
4318            el = Roo.fly(el);
4319            if(typeof styles == "string"){
4320                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4321                var matches;
4322                while ((matches = re.exec(styles)) != null){
4323                    el.setStyle(matches[1], matches[2]);
4324                }
4325            }else if (typeof styles == "object"){
4326                for (var style in styles){
4327                   el.setStyle(style, styles[style]);
4328                }
4329            }else if (typeof styles == "function"){
4330                 Roo.DomHelper.applyStyles(el, styles.call());
4331            }
4332         }
4333     },
4334
4335     /**
4336      * Inserts an HTML fragment into the Dom
4337      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4338      * @param {HTMLElement} el The context element
4339      * @param {String} html The HTML fragmenet
4340      * @return {HTMLElement} The new node
4341      */
4342     insertHtml : function(where, el, html){
4343         where = where.toLowerCase();
4344         if(el.insertAdjacentHTML){
4345             if(tableRe.test(el.tagName)){
4346                 var rs;
4347                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4348                     return rs;
4349                 }
4350             }
4351             switch(where){
4352                 case "beforebegin":
4353                     el.insertAdjacentHTML('BeforeBegin', html);
4354                     return el.previousSibling;
4355                 case "afterbegin":
4356                     el.insertAdjacentHTML('AfterBegin', html);
4357                     return el.firstChild;
4358                 case "beforeend":
4359                     el.insertAdjacentHTML('BeforeEnd', html);
4360                     return el.lastChild;
4361                 case "afterend":
4362                     el.insertAdjacentHTML('AfterEnd', html);
4363                     return el.nextSibling;
4364             }
4365             throw 'Illegal insertion point -> "' + where + '"';
4366         }
4367         var range = el.ownerDocument.createRange();
4368         var frag;
4369         switch(where){
4370              case "beforebegin":
4371                 range.setStartBefore(el);
4372                 frag = range.createContextualFragment(html);
4373                 el.parentNode.insertBefore(frag, el);
4374                 return el.previousSibling;
4375              case "afterbegin":
4376                 if(el.firstChild){
4377                     range.setStartBefore(el.firstChild);
4378                     frag = range.createContextualFragment(html);
4379                     el.insertBefore(frag, el.firstChild);
4380                     return el.firstChild;
4381                 }else{
4382                     el.innerHTML = html;
4383                     return el.firstChild;
4384                 }
4385             case "beforeend":
4386                 if(el.lastChild){
4387                     range.setStartAfter(el.lastChild);
4388                     frag = range.createContextualFragment(html);
4389                     el.appendChild(frag);
4390                     return el.lastChild;
4391                 }else{
4392                     el.innerHTML = html;
4393                     return el.lastChild;
4394                 }
4395             case "afterend":
4396                 range.setStartAfter(el);
4397                 frag = range.createContextualFragment(html);
4398                 el.parentNode.insertBefore(frag, el.nextSibling);
4399                 return el.nextSibling;
4400             }
4401             throw 'Illegal insertion point -> "' + where + '"';
4402     },
4403
4404     /**
4405      * Creates new Dom element(s) and inserts them before el
4406      * @param {String/HTMLElement/Element} el The context element
4407      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4408      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4409      * @return {HTMLElement/Roo.Element} The new node
4410      */
4411     insertBefore : function(el, o, returnElement){
4412         return this.doInsert(el, o, returnElement, "beforeBegin");
4413     },
4414
4415     /**
4416      * Creates new Dom element(s) and inserts them after el
4417      * @param {String/HTMLElement/Element} el The context element
4418      * @param {Object} o The Dom object spec (and children)
4419      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4420      * @return {HTMLElement/Roo.Element} The new node
4421      */
4422     insertAfter : function(el, o, returnElement){
4423         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4424     },
4425
4426     /**
4427      * Creates new Dom element(s) and inserts them as the first child of el
4428      * @param {String/HTMLElement/Element} el The context element
4429      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4430      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4431      * @return {HTMLElement/Roo.Element} The new node
4432      */
4433     insertFirst : function(el, o, returnElement){
4434         return this.doInsert(el, o, returnElement, "afterBegin");
4435     },
4436
4437     // private
4438     doInsert : function(el, o, returnElement, pos, sibling){
4439         el = Roo.getDom(el);
4440         var newNode;
4441         if(this.useDom || o.ns){
4442             newNode = createDom(o, null);
4443             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4444         }else{
4445             var html = createHtml(o);
4446             newNode = this.insertHtml(pos, el, html);
4447         }
4448         return returnElement ? Roo.get(newNode, true) : newNode;
4449     },
4450
4451     /**
4452      * Creates new Dom element(s) and appends them to el
4453      * @param {String/HTMLElement/Element} el The context element
4454      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4455      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4456      * @return {HTMLElement/Roo.Element} The new node
4457      */
4458     append : function(el, o, returnElement){
4459         el = Roo.getDom(el);
4460         var newNode;
4461         if(this.useDom || o.ns){
4462             newNode = createDom(o, null);
4463             el.appendChild(newNode);
4464         }else{
4465             var html = createHtml(o);
4466             newNode = this.insertHtml("beforeEnd", el, html);
4467         }
4468         return returnElement ? Roo.get(newNode, true) : newNode;
4469     },
4470
4471     /**
4472      * Creates new Dom element(s) and overwrites the contents of el with them
4473      * @param {String/HTMLElement/Element} el The context element
4474      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4475      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4476      * @return {HTMLElement/Roo.Element} The new node
4477      */
4478     overwrite : function(el, o, returnElement){
4479         el = Roo.getDom(el);
4480         if (o.ns) {
4481           
4482             while (el.childNodes.length) {
4483                 el.removeChild(el.firstChild);
4484             }
4485             createDom(o, el);
4486         } else {
4487             el.innerHTML = createHtml(o);   
4488         }
4489         
4490         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4491     },
4492
4493     /**
4494      * Creates a new Roo.DomHelper.Template from the Dom object spec
4495      * @param {Object} o The Dom object spec (and children)
4496      * @return {Roo.DomHelper.Template} The new template
4497      */
4498     createTemplate : function(o){
4499         var html = createHtml(o);
4500         return new Roo.Template(html);
4501     }
4502     };
4503 }();
4504 /*
4505  * Based on:
4506  * Ext JS Library 1.1.1
4507  * Copyright(c) 2006-2007, Ext JS, LLC.
4508  *
4509  * Originally Released Under LGPL - original licence link has changed is not relivant.
4510  *
4511  * Fork - LGPL
4512  * <script type="text/javascript">
4513  */
4514  
4515 /**
4516 * @class Roo.Template
4517 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4518 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4519 * Usage:
4520 <pre><code>
4521 var t = new Roo.Template({
4522     html :  '&lt;div name="{id}"&gt;' + 
4523         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4524         '&lt;/div&gt;',
4525     myformat: function (value, allValues) {
4526         return 'XX' + value;
4527     }
4528 });
4529 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4530 </code></pre>
4531 * For more information see this blog post with examples: <a href="http://www.jackslocum.com/yui/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">DomHelper - Create Elements using DOM, HTML fragments and Templates</a>. 
4532 * @constructor
4533 * @param {Object} cfg - Configuration object.
4534 */
4535 Roo.Template = function(cfg){
4536     // BC!
4537     if(cfg instanceof Array){
4538         cfg = cfg.join("");
4539     }else if(arguments.length > 1){
4540         cfg = Array.prototype.join.call(arguments, "");
4541     }
4542     
4543     
4544     if (typeof(cfg) == 'object') {
4545         Roo.apply(this,cfg)
4546     } else {
4547         // bc
4548         this.html = cfg;
4549     }
4550     
4551     
4552 };
4553 Roo.Template.prototype = {
4554     
4555     /**
4556      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4557      */
4558     html : '',
4559     /**
4560      * Returns an HTML fragment of this template with the specified values applied.
4561      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4562      * @return {String} The HTML fragment
4563      */
4564     applyTemplate : function(values){
4565         try {
4566             
4567             if(this.compiled){
4568                 return this.compiled(values);
4569             }
4570             var useF = this.disableFormats !== true;
4571             var fm = Roo.util.Format, tpl = this;
4572             var fn = function(m, name, format, args){
4573                 if(format && useF){
4574                     if(format.substr(0, 5) == "this."){
4575                         return tpl.call(format.substr(5), values[name], values);
4576                     }else{
4577                         if(args){
4578                             // quoted values are required for strings in compiled templates, 
4579                             // but for non compiled we need to strip them
4580                             // quoted reversed for jsmin
4581                             var re = /^\s*['"](.*)["']\s*$/;
4582                             args = args.split(',');
4583                             for(var i = 0, len = args.length; i < len; i++){
4584                                 args[i] = args[i].replace(re, "$1");
4585                             }
4586                             args = [values[name]].concat(args);
4587                         }else{
4588                             args = [values[name]];
4589                         }
4590                         return fm[format].apply(fm, args);
4591                     }
4592                 }else{
4593                     return values[name] !== undefined ? values[name] : "";
4594                 }
4595             };
4596             return this.html.replace(this.re, fn);
4597         } catch (e) {
4598             Roo.log(e);
4599             throw e;
4600         }
4601          
4602     },
4603     
4604     /**
4605      * Sets the HTML used as the template and optionally compiles it.
4606      * @param {String} html
4607      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4608      * @return {Roo.Template} this
4609      */
4610     set : function(html, compile){
4611         this.html = html;
4612         this.compiled = null;
4613         if(compile){
4614             this.compile();
4615         }
4616         return this;
4617     },
4618     
4619     /**
4620      * True to disable format functions (defaults to false)
4621      * @type Boolean
4622      */
4623     disableFormats : false,
4624     
4625     /**
4626     * The regular expression used to match template variables 
4627     * @type RegExp
4628     * @property 
4629     */
4630     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4631     
4632     /**
4633      * Compiles the template into an internal function, eliminating the RegEx overhead.
4634      * @return {Roo.Template} this
4635      */
4636     compile : function(){
4637         var fm = Roo.util.Format;
4638         var useF = this.disableFormats !== true;
4639         var sep = Roo.isGecko ? "+" : ",";
4640         var fn = function(m, name, format, args){
4641             if(format && useF){
4642                 args = args ? ',' + args : "";
4643                 if(format.substr(0, 5) != "this."){
4644                     format = "fm." + format + '(';
4645                 }else{
4646                     format = 'this.call("'+ format.substr(5) + '", ';
4647                     args = ", values";
4648                 }
4649             }else{
4650                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4651             }
4652             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4653         };
4654         var body;
4655         // branched to use + in gecko and [].join() in others
4656         if(Roo.isGecko){
4657             body = "this.compiled = function(values){ return '" +
4658                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4659                     "';};";
4660         }else{
4661             body = ["this.compiled = function(values){ return ['"];
4662             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4663             body.push("'].join('');};");
4664             body = body.join('');
4665         }
4666         /**
4667          * eval:var:values
4668          * eval:var:fm
4669          */
4670         eval(body);
4671         return this;
4672     },
4673     
4674     // private function used to call members
4675     call : function(fnName, value, allValues){
4676         return this[fnName](value, allValues);
4677     },
4678     
4679     /**
4680      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4681      * @param {String/HTMLElement/Roo.Element} el The context element
4682      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4683      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4684      * @return {HTMLElement/Roo.Element} The new node or Element
4685      */
4686     insertFirst: function(el, values, returnElement){
4687         return this.doInsert('afterBegin', el, values, returnElement);
4688     },
4689
4690     /**
4691      * Applies the supplied values to the template and inserts the new node(s) before el.
4692      * @param {String/HTMLElement/Roo.Element} el The context element
4693      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4694      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4695      * @return {HTMLElement/Roo.Element} The new node or Element
4696      */
4697     insertBefore: function(el, values, returnElement){
4698         return this.doInsert('beforeBegin', el, values, returnElement);
4699     },
4700
4701     /**
4702      * Applies the supplied values to the template and inserts the new node(s) after el.
4703      * @param {String/HTMLElement/Roo.Element} el The context element
4704      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4705      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4706      * @return {HTMLElement/Roo.Element} The new node or Element
4707      */
4708     insertAfter : function(el, values, returnElement){
4709         return this.doInsert('afterEnd', el, values, returnElement);
4710     },
4711     
4712     /**
4713      * Applies the supplied values to the template and appends the new node(s) to el.
4714      * @param {String/HTMLElement/Roo.Element} el The context element
4715      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4716      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4717      * @return {HTMLElement/Roo.Element} The new node or Element
4718      */
4719     append : function(el, values, returnElement){
4720         return this.doInsert('beforeEnd', el, values, returnElement);
4721     },
4722
4723     doInsert : function(where, el, values, returnEl){
4724         el = Roo.getDom(el);
4725         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4726         return returnEl ? Roo.get(newNode, true) : newNode;
4727     },
4728
4729     /**
4730      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4731      * @param {String/HTMLElement/Roo.Element} el The context element
4732      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4733      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4734      * @return {HTMLElement/Roo.Element} The new node or Element
4735      */
4736     overwrite : function(el, values, returnElement){
4737         el = Roo.getDom(el);
4738         el.innerHTML = this.applyTemplate(values);
4739         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4740     }
4741 };
4742 /**
4743  * Alias for {@link #applyTemplate}
4744  * @method
4745  */
4746 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4747
4748 // backwards compat
4749 Roo.DomHelper.Template = Roo.Template;
4750
4751 /**
4752  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4753  * @param {String/HTMLElement} el A DOM element or its id
4754  * @returns {Roo.Template} The created template
4755  * @static
4756  */
4757 Roo.Template.from = function(el){
4758     el = Roo.getDom(el);
4759     return new Roo.Template(el.value || el.innerHTML);
4760 };/*
4761  * Based on:
4762  * Ext JS Library 1.1.1
4763  * Copyright(c) 2006-2007, Ext JS, LLC.
4764  *
4765  * Originally Released Under LGPL - original licence link has changed is not relivant.
4766  *
4767  * Fork - LGPL
4768  * <script type="text/javascript">
4769  */
4770  
4771
4772 /*
4773  * This is code is also distributed under MIT license for use
4774  * with jQuery and prototype JavaScript libraries.
4775  */
4776 /**
4777  * @class Roo.DomQuery
4778 Provides high performance selector/xpath processing by compiling queries into reusable functions. New pseudo classes and matchers can be plugged. It works on HTML and XML documents (if a content node is passed in).
4779 <p>
4780 DomQuery supports most of the <a href="http://www.w3.org/TR/2005/WD-css3-selectors-20051215/">CSS3 selectors spec</a>, along with some custom selectors and basic XPath.</p>
4781
4782 <p>
4783 All selectors, attribute filters and pseudos below can be combined infinitely in any order. For example "div.foo:nth-child(odd)[@foo=bar].bar:first" would be a perfectly valid selector. Node filters are processed in the order in which they appear, which allows you to optimize your queries for your document structure.
4784 </p>
4785 <h4>Element Selectors:</h4>
4786 <ul class="list">
4787     <li> <b>*</b> any element</li>
4788     <li> <b>E</b> an element with the tag E</li>
4789     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4790     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4791     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4792     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4793 </ul>
4794 <h4>Attribute Selectors:</h4>
4795 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4796 <ul class="list">
4797     <li> <b>E[foo]</b> has an attribute "foo"</li>
4798     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4799     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4800     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4801     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4802     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4803     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4804 </ul>
4805 <h4>Pseudo Classes:</h4>
4806 <ul class="list">
4807     <li> <b>E:first-child</b> E is the first child of its parent</li>
4808     <li> <b>E:last-child</b> E is the last child of its parent</li>
4809     <li> <b>E:nth-child(<i>n</i>)</b> E is the <i>n</i>th child of its parent (1 based as per the spec)</li>
4810     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4811     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4812     <li> <b>E:only-child</b> E is the only child of its parent</li>
4813     <li> <b>E:checked</b> E is an element that is has a checked attribute that is true (e.g. a radio or checkbox) </li>
4814     <li> <b>E:first</b> the first E in the resultset</li>
4815     <li> <b>E:last</b> the last E in the resultset</li>
4816     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4817     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4818     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4819     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4820     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4821     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4822     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4823     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4824     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4825 </ul>
4826 <h4>CSS Value Selectors:</h4>
4827 <ul class="list">
4828     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4829     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4830     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4831     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4832     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4833     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4834 </ul>
4835  * @singleton
4836  */
4837 Roo.DomQuery = function(){
4838     var cache = {}, simpleCache = {}, valueCache = {};
4839     var nonSpace = /\S/;
4840     var trimRe = /^\s+|\s+$/g;
4841     var tplRe = /\{(\d+)\}/g;
4842     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4843     var tagTokenRe = /^(#)?([\w-\*]+)/;
4844     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4845
4846     function child(p, index){
4847         var i = 0;
4848         var n = p.firstChild;
4849         while(n){
4850             if(n.nodeType == 1){
4851                if(++i == index){
4852                    return n;
4853                }
4854             }
4855             n = n.nextSibling;
4856         }
4857         return null;
4858     };
4859
4860     function next(n){
4861         while((n = n.nextSibling) && n.nodeType != 1);
4862         return n;
4863     };
4864
4865     function prev(n){
4866         while((n = n.previousSibling) && n.nodeType != 1);
4867         return n;
4868     };
4869
4870     function children(d){
4871         var n = d.firstChild, ni = -1;
4872             while(n){
4873                 var nx = n.nextSibling;
4874                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
4875                     d.removeChild(n);
4876                 }else{
4877                     n.nodeIndex = ++ni;
4878                 }
4879                 n = nx;
4880             }
4881             return this;
4882         };
4883
4884     function byClassName(c, a, v){
4885         if(!v){
4886             return c;
4887         }
4888         var r = [], ri = -1, cn;
4889         for(var i = 0, ci; ci = c[i]; i++){
4890             if((' '+ci.className+' ').indexOf(v) != -1){
4891                 r[++ri] = ci;
4892             }
4893         }
4894         return r;
4895     };
4896
4897     function attrValue(n, attr){
4898         if(!n.tagName && typeof n.length != "undefined"){
4899             n = n[0];
4900         }
4901         if(!n){
4902             return null;
4903         }
4904         if(attr == "for"){
4905             return n.htmlFor;
4906         }
4907         if(attr == "class" || attr == "className"){
4908             return n.className;
4909         }
4910         return n.getAttribute(attr) || n[attr];
4911
4912     };
4913
4914     function getNodes(ns, mode, tagName){
4915         var result = [], ri = -1, cs;
4916         if(!ns){
4917             return result;
4918         }
4919         tagName = tagName || "*";
4920         if(typeof ns.getElementsByTagName != "undefined"){
4921             ns = [ns];
4922         }
4923         if(!mode){
4924             for(var i = 0, ni; ni = ns[i]; i++){
4925                 cs = ni.getElementsByTagName(tagName);
4926                 for(var j = 0, ci; ci = cs[j]; j++){
4927                     result[++ri] = ci;
4928                 }
4929             }
4930         }else if(mode == "/" || mode == ">"){
4931             var utag = tagName.toUpperCase();
4932             for(var i = 0, ni, cn; ni = ns[i]; i++){
4933                 cn = ni.children || ni.childNodes;
4934                 for(var j = 0, cj; cj = cn[j]; j++){
4935                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
4936                         result[++ri] = cj;
4937                     }
4938                 }
4939             }
4940         }else if(mode == "+"){
4941             var utag = tagName.toUpperCase();
4942             for(var i = 0, n; n = ns[i]; i++){
4943                 while((n = n.nextSibling) && n.nodeType != 1);
4944                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
4945                     result[++ri] = n;
4946                 }
4947             }
4948         }else if(mode == "~"){
4949             for(var i = 0, n; n = ns[i]; i++){
4950                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
4951                 if(n){
4952                     result[++ri] = n;
4953                 }
4954             }
4955         }
4956         return result;
4957     };
4958
4959     function concat(a, b){
4960         if(b.slice){
4961             return a.concat(b);
4962         }
4963         for(var i = 0, l = b.length; i < l; i++){
4964             a[a.length] = b[i];
4965         }
4966         return a;
4967     }
4968
4969     function byTag(cs, tagName){
4970         if(cs.tagName || cs == document){
4971             cs = [cs];
4972         }
4973         if(!tagName){
4974             return cs;
4975         }
4976         var r = [], ri = -1;
4977         tagName = tagName.toLowerCase();
4978         for(var i = 0, ci; ci = cs[i]; i++){
4979             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
4980                 r[++ri] = ci;
4981             }
4982         }
4983         return r;
4984     };
4985
4986     function byId(cs, attr, id){
4987         if(cs.tagName || cs == document){
4988             cs = [cs];
4989         }
4990         if(!id){
4991             return cs;
4992         }
4993         var r = [], ri = -1;
4994         for(var i = 0,ci; ci = cs[i]; i++){
4995             if(ci && ci.id == id){
4996                 r[++ri] = ci;
4997                 return r;
4998             }
4999         }
5000         return r;
5001     };
5002
5003     function byAttribute(cs, attr, value, op, custom){
5004         var r = [], ri = -1, st = custom=="{";
5005         var f = Roo.DomQuery.operators[op];
5006         for(var i = 0, ci; ci = cs[i]; i++){
5007             var a;
5008             if(st){
5009                 a = Roo.DomQuery.getStyle(ci, attr);
5010             }
5011             else if(attr == "class" || attr == "className"){
5012                 a = ci.className;
5013             }else if(attr == "for"){
5014                 a = ci.htmlFor;
5015             }else if(attr == "href"){
5016                 a = ci.getAttribute("href", 2);
5017             }else{
5018                 a = ci.getAttribute(attr);
5019             }
5020             if((f && f(a, value)) || (!f && a)){
5021                 r[++ri] = ci;
5022             }
5023         }
5024         return r;
5025     };
5026
5027     function byPseudo(cs, name, value){
5028         return Roo.DomQuery.pseudos[name](cs, value);
5029     };
5030
5031     // This is for IE MSXML which does not support expandos.
5032     // IE runs the same speed using setAttribute, however FF slows way down
5033     // and Safari completely fails so they need to continue to use expandos.
5034     var isIE = window.ActiveXObject ? true : false;
5035
5036     // this eval is stop the compressor from
5037     // renaming the variable to something shorter
5038     
5039     /** eval:var:batch */
5040     var batch = 30803; 
5041
5042     var key = 30803;
5043
5044     function nodupIEXml(cs){
5045         var d = ++key;
5046         cs[0].setAttribute("_nodup", d);
5047         var r = [cs[0]];
5048         for(var i = 1, len = cs.length; i < len; i++){
5049             var c = cs[i];
5050             if(!c.getAttribute("_nodup") != d){
5051                 c.setAttribute("_nodup", d);
5052                 r[r.length] = c;
5053             }
5054         }
5055         for(var i = 0, len = cs.length; i < len; i++){
5056             cs[i].removeAttribute("_nodup");
5057         }
5058         return r;
5059     }
5060
5061     function nodup(cs){
5062         if(!cs){
5063             return [];
5064         }
5065         var len = cs.length, c, i, r = cs, cj, ri = -1;
5066         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5067             return cs;
5068         }
5069         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5070             return nodupIEXml(cs);
5071         }
5072         var d = ++key;
5073         cs[0]._nodup = d;
5074         for(i = 1; c = cs[i]; i++){
5075             if(c._nodup != d){
5076                 c._nodup = d;
5077             }else{
5078                 r = [];
5079                 for(var j = 0; j < i; j++){
5080                     r[++ri] = cs[j];
5081                 }
5082                 for(j = i+1; cj = cs[j]; j++){
5083                     if(cj._nodup != d){
5084                         cj._nodup = d;
5085                         r[++ri] = cj;
5086                     }
5087                 }
5088                 return r;
5089             }
5090         }
5091         return r;
5092     }
5093
5094     function quickDiffIEXml(c1, c2){
5095         var d = ++key;
5096         for(var i = 0, len = c1.length; i < len; i++){
5097             c1[i].setAttribute("_qdiff", d);
5098         }
5099         var r = [];
5100         for(var i = 0, len = c2.length; i < len; i++){
5101             if(c2[i].getAttribute("_qdiff") != d){
5102                 r[r.length] = c2[i];
5103             }
5104         }
5105         for(var i = 0, len = c1.length; i < len; i++){
5106            c1[i].removeAttribute("_qdiff");
5107         }
5108         return r;
5109     }
5110
5111     function quickDiff(c1, c2){
5112         var len1 = c1.length;
5113         if(!len1){
5114             return c2;
5115         }
5116         if(isIE && c1[0].selectSingleNode){
5117             return quickDiffIEXml(c1, c2);
5118         }
5119         var d = ++key;
5120         for(var i = 0; i < len1; i++){
5121             c1[i]._qdiff = d;
5122         }
5123         var r = [];
5124         for(var i = 0, len = c2.length; i < len; i++){
5125             if(c2[i]._qdiff != d){
5126                 r[r.length] = c2[i];
5127             }
5128         }
5129         return r;
5130     }
5131
5132     function quickId(ns, mode, root, id){
5133         if(ns == root){
5134            var d = root.ownerDocument || root;
5135            return d.getElementById(id);
5136         }
5137         ns = getNodes(ns, mode, "*");
5138         return byId(ns, null, id);
5139     }
5140
5141     return {
5142         getStyle : function(el, name){
5143             return Roo.fly(el).getStyle(name);
5144         },
5145         /**
5146          * Compiles a selector/xpath query into a reusable function. The returned function
5147          * takes one parameter "root" (optional), which is the context node from where the query should start.
5148          * @param {String} selector The selector/xpath query
5149          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5150          * @return {Function}
5151          */
5152         compile : function(path, type){
5153             type = type || "select";
5154             
5155             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5156             var q = path, mode, lq;
5157             var tk = Roo.DomQuery.matchers;
5158             var tklen = tk.length;
5159             var mm;
5160
5161             // accept leading mode switch
5162             var lmode = q.match(modeRe);
5163             if(lmode && lmode[1]){
5164                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5165                 q = q.replace(lmode[1], "");
5166             }
5167             // strip leading slashes
5168             while(path.substr(0, 1)=="/"){
5169                 path = path.substr(1);
5170             }
5171
5172             while(q && lq != q){
5173                 lq = q;
5174                 var tm = q.match(tagTokenRe);
5175                 if(type == "select"){
5176                     if(tm){
5177                         if(tm[1] == "#"){
5178                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5179                         }else{
5180                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5181                         }
5182                         q = q.replace(tm[0], "");
5183                     }else if(q.substr(0, 1) != '@'){
5184                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5185                     }
5186                 }else{
5187                     if(tm){
5188                         if(tm[1] == "#"){
5189                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5190                         }else{
5191                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5192                         }
5193                         q = q.replace(tm[0], "");
5194                     }
5195                 }
5196                 while(!(mm = q.match(modeRe))){
5197                     var matched = false;
5198                     for(var j = 0; j < tklen; j++){
5199                         var t = tk[j];
5200                         var m = q.match(t.re);
5201                         if(m){
5202                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5203                                                     return m[i];
5204                                                 });
5205                             q = q.replace(m[0], "");
5206                             matched = true;
5207                             break;
5208                         }
5209                     }
5210                     // prevent infinite loop on bad selector
5211                     if(!matched){
5212                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5213                     }
5214                 }
5215                 if(mm[1]){
5216                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5217                     q = q.replace(mm[1], "");
5218                 }
5219             }
5220             fn[fn.length] = "return nodup(n);\n}";
5221             
5222              /** 
5223               * list of variables that need from compression as they are used by eval.
5224              *  eval:var:batch 
5225              *  eval:var:nodup
5226              *  eval:var:byTag
5227              *  eval:var:ById
5228              *  eval:var:getNodes
5229              *  eval:var:quickId
5230              *  eval:var:mode
5231              *  eval:var:root
5232              *  eval:var:n
5233              *  eval:var:byClassName
5234              *  eval:var:byPseudo
5235              *  eval:var:byAttribute
5236              *  eval:var:attrValue
5237              * 
5238              **/ 
5239             eval(fn.join(""));
5240             return f;
5241         },
5242
5243         /**
5244          * Selects a group of elements.
5245          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5246          * @param {Node} root (optional) The start of the query (defaults to document).
5247          * @return {Array}
5248          */
5249         select : function(path, root, type){
5250             if(!root || root == document){
5251                 root = document;
5252             }
5253             if(typeof root == "string"){
5254                 root = document.getElementById(root);
5255             }
5256             var paths = path.split(",");
5257             var results = [];
5258             for(var i = 0, len = paths.length; i < len; i++){
5259                 var p = paths[i].replace(trimRe, "");
5260                 if(!cache[p]){
5261                     cache[p] = Roo.DomQuery.compile(p);
5262                     if(!cache[p]){
5263                         throw p + " is not a valid selector";
5264                     }
5265                 }
5266                 var result = cache[p](root);
5267                 if(result && result != document){
5268                     results = results.concat(result);
5269                 }
5270             }
5271             if(paths.length > 1){
5272                 return nodup(results);
5273             }
5274             return results;
5275         },
5276
5277         /**
5278          * Selects a single element.
5279          * @param {String} selector The selector/xpath query
5280          * @param {Node} root (optional) The start of the query (defaults to document).
5281          * @return {Element}
5282          */
5283         selectNode : function(path, root){
5284             return Roo.DomQuery.select(path, root)[0];
5285         },
5286
5287         /**
5288          * Selects the value of a node, optionally replacing null with the defaultValue.
5289          * @param {String} selector The selector/xpath query
5290          * @param {Node} root (optional) The start of the query (defaults to document).
5291          * @param {String} defaultValue
5292          */
5293         selectValue : function(path, root, defaultValue){
5294             path = path.replace(trimRe, "");
5295             if(!valueCache[path]){
5296                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5297             }
5298             var n = valueCache[path](root);
5299             n = n[0] ? n[0] : n;
5300             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5301             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5302         },
5303
5304         /**
5305          * Selects the value of a node, parsing integers and floats.
5306          * @param {String} selector The selector/xpath query
5307          * @param {Node} root (optional) The start of the query (defaults to document).
5308          * @param {Number} defaultValue
5309          * @return {Number}
5310          */
5311         selectNumber : function(path, root, defaultValue){
5312             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5313             return parseFloat(v);
5314         },
5315
5316         /**
5317          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5318          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5319          * @param {String} selector The simple selector to test
5320          * @return {Boolean}
5321          */
5322         is : function(el, ss){
5323             if(typeof el == "string"){
5324                 el = document.getElementById(el);
5325             }
5326             var isArray = (el instanceof Array);
5327             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5328             return isArray ? (result.length == el.length) : (result.length > 0);
5329         },
5330
5331         /**
5332          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5333          * @param {Array} el An array of elements to filter
5334          * @param {String} selector The simple selector to test
5335          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5336          * the selector instead of the ones that match
5337          * @return {Array}
5338          */
5339         filter : function(els, ss, nonMatches){
5340             ss = ss.replace(trimRe, "");
5341             if(!simpleCache[ss]){
5342                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5343             }
5344             var result = simpleCache[ss](els);
5345             return nonMatches ? quickDiff(result, els) : result;
5346         },
5347
5348         /**
5349          * Collection of matching regular expressions and code snippets.
5350          */
5351         matchers : [{
5352                 re: /^\.([\w-]+)/,
5353                 select: 'n = byClassName(n, null, " {1} ");'
5354             }, {
5355                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5356                 select: 'n = byPseudo(n, "{1}", "{2}");'
5357             },{
5358                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5359                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5360             }, {
5361                 re: /^#([\w-]+)/,
5362                 select: 'n = byId(n, null, "{1}");'
5363             },{
5364                 re: /^@([\w-]+)/,
5365                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5366             }
5367         ],
5368
5369         /**
5370          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5371          * New operators can be added as long as the match the format <i>c</i>= where <i>c</i> is any character other than space, &gt; &lt;.
5372          */
5373         operators : {
5374             "=" : function(a, v){
5375                 return a == v;
5376             },
5377             "!=" : function(a, v){
5378                 return a != v;
5379             },
5380             "^=" : function(a, v){
5381                 return a && a.substr(0, v.length) == v;
5382             },
5383             "$=" : function(a, v){
5384                 return a && a.substr(a.length-v.length) == v;
5385             },
5386             "*=" : function(a, v){
5387                 return a && a.indexOf(v) !== -1;
5388             },
5389             "%=" : function(a, v){
5390                 return (a % v) == 0;
5391             },
5392             "|=" : function(a, v){
5393                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5394             },
5395             "~=" : function(a, v){
5396                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5397             }
5398         },
5399
5400         /**
5401          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5402          * and the argument (if any) supplied in the selector.
5403          */
5404         pseudos : {
5405             "first-child" : function(c){
5406                 var r = [], ri = -1, n;
5407                 for(var i = 0, ci; ci = n = c[i]; i++){
5408                     while((n = n.previousSibling) && n.nodeType != 1);
5409                     if(!n){
5410                         r[++ri] = ci;
5411                     }
5412                 }
5413                 return r;
5414             },
5415
5416             "last-child" : function(c){
5417                 var r = [], ri = -1, n;
5418                 for(var i = 0, ci; ci = n = c[i]; i++){
5419                     while((n = n.nextSibling) && n.nodeType != 1);
5420                     if(!n){
5421                         r[++ri] = ci;
5422                     }
5423                 }
5424                 return r;
5425             },
5426
5427             "nth-child" : function(c, a) {
5428                 var r = [], ri = -1;
5429                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5430                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5431                 for(var i = 0, n; n = c[i]; i++){
5432                     var pn = n.parentNode;
5433                     if (batch != pn._batch) {
5434                         var j = 0;
5435                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5436                             if(cn.nodeType == 1){
5437                                cn.nodeIndex = ++j;
5438                             }
5439                         }
5440                         pn._batch = batch;
5441                     }
5442                     if (f == 1) {
5443                         if (l == 0 || n.nodeIndex == l){
5444                             r[++ri] = n;
5445                         }
5446                     } else if ((n.nodeIndex + l) % f == 0){
5447                         r[++ri] = n;
5448                     }
5449                 }
5450
5451                 return r;
5452             },
5453
5454             "only-child" : function(c){
5455                 var r = [], ri = -1;;
5456                 for(var i = 0, ci; ci = c[i]; i++){
5457                     if(!prev(ci) && !next(ci)){
5458                         r[++ri] = ci;
5459                     }
5460                 }
5461                 return r;
5462             },
5463
5464             "empty" : function(c){
5465                 var r = [], ri = -1;
5466                 for(var i = 0, ci; ci = c[i]; i++){
5467                     var cns = ci.childNodes, j = 0, cn, empty = true;
5468                     while(cn = cns[j]){
5469                         ++j;
5470                         if(cn.nodeType == 1 || cn.nodeType == 3){
5471                             empty = false;
5472                             break;
5473                         }
5474                     }
5475                     if(empty){
5476                         r[++ri] = ci;
5477                     }
5478                 }
5479                 return r;
5480             },
5481
5482             "contains" : function(c, v){
5483                 var r = [], ri = -1;
5484                 for(var i = 0, ci; ci = c[i]; i++){
5485                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5486                         r[++ri] = ci;
5487                     }
5488                 }
5489                 return r;
5490             },
5491
5492             "nodeValue" : function(c, v){
5493                 var r = [], ri = -1;
5494                 for(var i = 0, ci; ci = c[i]; i++){
5495                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5496                         r[++ri] = ci;
5497                     }
5498                 }
5499                 return r;
5500             },
5501
5502             "checked" : function(c){
5503                 var r = [], ri = -1;
5504                 for(var i = 0, ci; ci = c[i]; i++){
5505                     if(ci.checked == true){
5506                         r[++ri] = ci;
5507                     }
5508                 }
5509                 return r;
5510             },
5511
5512             "not" : function(c, ss){
5513                 return Roo.DomQuery.filter(c, ss, true);
5514             },
5515
5516             "odd" : function(c){
5517                 return this["nth-child"](c, "odd");
5518             },
5519
5520             "even" : function(c){
5521                 return this["nth-child"](c, "even");
5522             },
5523
5524             "nth" : function(c, a){
5525                 return c[a-1] || [];
5526             },
5527
5528             "first" : function(c){
5529                 return c[0] || [];
5530             },
5531
5532             "last" : function(c){
5533                 return c[c.length-1] || [];
5534             },
5535
5536             "has" : function(c, ss){
5537                 var s = Roo.DomQuery.select;
5538                 var r = [], ri = -1;
5539                 for(var i = 0, ci; ci = c[i]; i++){
5540                     if(s(ss, ci).length > 0){
5541                         r[++ri] = ci;
5542                     }
5543                 }
5544                 return r;
5545             },
5546
5547             "next" : function(c, ss){
5548                 var is = Roo.DomQuery.is;
5549                 var r = [], ri = -1;
5550                 for(var i = 0, ci; ci = c[i]; i++){
5551                     var n = next(ci);
5552                     if(n && is(n, ss)){
5553                         r[++ri] = ci;
5554                     }
5555                 }
5556                 return r;
5557             },
5558
5559             "prev" : function(c, ss){
5560                 var is = Roo.DomQuery.is;
5561                 var r = [], ri = -1;
5562                 for(var i = 0, ci; ci = c[i]; i++){
5563                     var n = prev(ci);
5564                     if(n && is(n, ss)){
5565                         r[++ri] = ci;
5566                     }
5567                 }
5568                 return r;
5569             }
5570         }
5571     };
5572 }();
5573
5574 /**
5575  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5576  * @param {String} path The selector/xpath query
5577  * @param {Node} root (optional) The start of the query (defaults to document).
5578  * @return {Array}
5579  * @member Roo
5580  * @method query
5581  */
5582 Roo.query = Roo.DomQuery.select;
5583 /*
5584  * Based on:
5585  * Ext JS Library 1.1.1
5586  * Copyright(c) 2006-2007, Ext JS, LLC.
5587  *
5588  * Originally Released Under LGPL - original licence link has changed is not relivant.
5589  *
5590  * Fork - LGPL
5591  * <script type="text/javascript">
5592  */
5593
5594 /**
5595  * @class Roo.util.Observable
5596  * Base class that provides a common interface for publishing events. Subclasses are expected to
5597  * to have a property "events" with all the events defined.<br>
5598  * For example:
5599  * <pre><code>
5600  Employee = function(name){
5601     this.name = name;
5602     this.addEvents({
5603         "fired" : true,
5604         "quit" : true
5605     });
5606  }
5607  Roo.extend(Employee, Roo.util.Observable);
5608 </code></pre>
5609  * @param {Object} config properties to use (incuding events / listeners)
5610  */
5611
5612 Roo.util.Observable = function(cfg){
5613     
5614     cfg = cfg|| {};
5615     this.addEvents(cfg.events || {});
5616     if (cfg.events) {
5617         delete cfg.events; // make sure
5618     }
5619      
5620     Roo.apply(this, cfg);
5621     
5622     if(this.listeners){
5623         this.on(this.listeners);
5624         delete this.listeners;
5625     }
5626 };
5627 Roo.util.Observable.prototype = {
5628     /** 
5629  * @cfg {Object} listeners  list of events and functions to call for this object, 
5630  * For example :
5631  * <pre><code>
5632     listeners :  { 
5633        'click' : function(e) {
5634            ..... 
5635         } ,
5636         .... 
5637     } 
5638   </code></pre>
5639  */
5640     
5641     
5642     /**
5643      * Fires the specified event with the passed parameters (minus the event name).
5644      * @param {String} eventName
5645      * @param {Object...} args Variable number of parameters are passed to handlers
5646      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5647      */
5648     fireEvent : function(){
5649         var ce = this.events[arguments[0].toLowerCase()];
5650         if(typeof ce == "object"){
5651             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5652         }else{
5653             return true;
5654         }
5655     },
5656
5657     // private
5658     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5659
5660     /**
5661      * Appends an event handler to this component
5662      * @param {String}   eventName The type of event to listen for
5663      * @param {Function} handler The method the event invokes
5664      * @param {Object}   scope (optional) The scope in which to execute the handler
5665      * function. The handler function's "this" context.
5666      * @param {Object}   options (optional) An object containing handler configuration
5667      * properties. This may contain any of the following properties:<ul>
5668      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5669      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5670      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5671      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5672      * by the specified number of milliseconds. If the event fires again within that time, the original
5673      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5674      * </ul><br>
5675      * <p>
5676      * <b>Combining Options</b><br>
5677      * Using the options argument, it is possible to combine different types of listeners:<br>
5678      * <br>
5679      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5680                 <pre><code>
5681                 el.on('click', this.onClick, this, {
5682                         single: true,
5683                 delay: 100,
5684                 forumId: 4
5685                 });
5686                 </code></pre>
5687      * <p>
5688      * <b>Attaching multiple handlers in 1 call</b><br>
5689      * The method also allows for a single argument to be passed which is a config object containing properties
5690      * which specify multiple handlers.
5691      * <pre><code>
5692                 el.on({
5693                         'click': {
5694                         fn: this.onClick,
5695                         scope: this,
5696                         delay: 100
5697                 }, 
5698                 'mouseover': {
5699                         fn: this.onMouseOver,
5700                         scope: this
5701                 },
5702                 'mouseout': {
5703                         fn: this.onMouseOut,
5704                         scope: this
5705                 }
5706                 });
5707                 </code></pre>
5708      * <p>
5709      * Or a shorthand syntax which passes the same scope object to all handlers:
5710         <pre><code>
5711                 el.on({
5712                         'click': this.onClick,
5713                 'mouseover': this.onMouseOver,
5714                 'mouseout': this.onMouseOut,
5715                 scope: this
5716                 });
5717                 </code></pre>
5718      */
5719     addListener : function(eventName, fn, scope, o){
5720         if(typeof eventName == "object"){
5721             o = eventName;
5722             for(var e in o){
5723                 if(this.filterOptRe.test(e)){
5724                     continue;
5725                 }
5726                 if(typeof o[e] == "function"){
5727                     // shared options
5728                     this.addListener(e, o[e], o.scope,  o);
5729                 }else{
5730                     // individual options
5731                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5732                 }
5733             }
5734             return;
5735         }
5736         o = (!o || typeof o == "boolean") ? {} : o;
5737         eventName = eventName.toLowerCase();
5738         var ce = this.events[eventName] || true;
5739         if(typeof ce == "boolean"){
5740             ce = new Roo.util.Event(this, eventName);
5741             this.events[eventName] = ce;
5742         }
5743         ce.addListener(fn, scope, o);
5744     },
5745
5746     /**
5747      * Removes a listener
5748      * @param {String}   eventName     The type of event to listen for
5749      * @param {Function} handler        The handler to remove
5750      * @param {Object}   scope  (optional) The scope (this object) for the handler
5751      */
5752     removeListener : function(eventName, fn, scope){
5753         var ce = this.events[eventName.toLowerCase()];
5754         if(typeof ce == "object"){
5755             ce.removeListener(fn, scope);
5756         }
5757     },
5758
5759     /**
5760      * Removes all listeners for this object
5761      */
5762     purgeListeners : function(){
5763         for(var evt in this.events){
5764             if(typeof this.events[evt] == "object"){
5765                  this.events[evt].clearListeners();
5766             }
5767         }
5768     },
5769
5770     relayEvents : function(o, events){
5771         var createHandler = function(ename){
5772             return function(){
5773                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5774             };
5775         };
5776         for(var i = 0, len = events.length; i < len; i++){
5777             var ename = events[i];
5778             if(!this.events[ename]){ this.events[ename] = true; };
5779             o.on(ename, createHandler(ename), this);
5780         }
5781     },
5782
5783     /**
5784      * Used to define events on this Observable
5785      * @param {Object} object The object with the events defined
5786      */
5787     addEvents : function(o){
5788         if(!this.events){
5789             this.events = {};
5790         }
5791         Roo.applyIf(this.events, o);
5792     },
5793
5794     /**
5795      * Checks to see if this object has any listeners for a specified event
5796      * @param {String} eventName The name of the event to check for
5797      * @return {Boolean} True if the event is being listened for, else false
5798      */
5799     hasListener : function(eventName){
5800         var e = this.events[eventName];
5801         return typeof e == "object" && e.listeners.length > 0;
5802     }
5803 };
5804 /**
5805  * Appends an event handler to this element (shorthand for addListener)
5806  * @param {String}   eventName     The type of event to listen for
5807  * @param {Function} handler        The method the event invokes
5808  * @param {Object}   scope (optional) The scope in which to execute the handler
5809  * function. The handler function's "this" context.
5810  * @param {Object}   options  (optional)
5811  * @method
5812  */
5813 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5814 /**
5815  * Removes a listener (shorthand for removeListener)
5816  * @param {String}   eventName     The type of event to listen for
5817  * @param {Function} handler        The handler to remove
5818  * @param {Object}   scope  (optional) The scope (this object) for the handler
5819  * @method
5820  */
5821 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5822
5823 /**
5824  * Starts capture on the specified Observable. All events will be passed
5825  * to the supplied function with the event name + standard signature of the event
5826  * <b>before</b> the event is fired. If the supplied function returns false,
5827  * the event will not fire.
5828  * @param {Observable} o The Observable to capture
5829  * @param {Function} fn The function to call
5830  * @param {Object} scope (optional) The scope (this object) for the fn
5831  * @static
5832  */
5833 Roo.util.Observable.capture = function(o, fn, scope){
5834     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5835 };
5836
5837 /**
5838  * Removes <b>all</b> added captures from the Observable.
5839  * @param {Observable} o The Observable to release
5840  * @static
5841  */
5842 Roo.util.Observable.releaseCapture = function(o){
5843     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5844 };
5845
5846 (function(){
5847
5848     var createBuffered = function(h, o, scope){
5849         var task = new Roo.util.DelayedTask();
5850         return function(){
5851             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5852         };
5853     };
5854
5855     var createSingle = function(h, e, fn, scope){
5856         return function(){
5857             e.removeListener(fn, scope);
5858             return h.apply(scope, arguments);
5859         };
5860     };
5861
5862     var createDelayed = function(h, o, scope){
5863         return function(){
5864             var args = Array.prototype.slice.call(arguments, 0);
5865             setTimeout(function(){
5866                 h.apply(scope, args);
5867             }, o.delay || 10);
5868         };
5869     };
5870
5871     Roo.util.Event = function(obj, name){
5872         this.name = name;
5873         this.obj = obj;
5874         this.listeners = [];
5875     };
5876
5877     Roo.util.Event.prototype = {
5878         addListener : function(fn, scope, options){
5879             var o = options || {};
5880             scope = scope || this.obj;
5881             if(!this.isListening(fn, scope)){
5882                 var l = {fn: fn, scope: scope, options: o};
5883                 var h = fn;
5884                 if(o.delay){
5885                     h = createDelayed(h, o, scope);
5886                 }
5887                 if(o.single){
5888                     h = createSingle(h, this, fn, scope);
5889                 }
5890                 if(o.buffer){
5891                     h = createBuffered(h, o, scope);
5892                 }
5893                 l.fireFn = h;
5894                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
5895                     this.listeners.push(l);
5896                 }else{
5897                     this.listeners = this.listeners.slice(0);
5898                     this.listeners.push(l);
5899                 }
5900             }
5901         },
5902
5903         findListener : function(fn, scope){
5904             scope = scope || this.obj;
5905             var ls = this.listeners;
5906             for(var i = 0, len = ls.length; i < len; i++){
5907                 var l = ls[i];
5908                 if(l.fn == fn && l.scope == scope){
5909                     return i;
5910                 }
5911             }
5912             return -1;
5913         },
5914
5915         isListening : function(fn, scope){
5916             return this.findListener(fn, scope) != -1;
5917         },
5918
5919         removeListener : function(fn, scope){
5920             var index;
5921             if((index = this.findListener(fn, scope)) != -1){
5922                 if(!this.firing){
5923                     this.listeners.splice(index, 1);
5924                 }else{
5925                     this.listeners = this.listeners.slice(0);
5926                     this.listeners.splice(index, 1);
5927                 }
5928                 return true;
5929             }
5930             return false;
5931         },
5932
5933         clearListeners : function(){
5934             this.listeners = [];
5935         },
5936
5937         fire : function(){
5938             var ls = this.listeners, scope, len = ls.length;
5939             if(len > 0){
5940                 this.firing = true;
5941                 var args = Array.prototype.slice.call(arguments, 0);
5942                 for(var i = 0; i < len; i++){
5943                     var l = ls[i];
5944                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
5945                         this.firing = false;
5946                         return false;
5947                     }
5948                 }
5949                 this.firing = false;
5950             }
5951             return true;
5952         }
5953     };
5954 })();/*
5955  * Based on:
5956  * Ext JS Library 1.1.1
5957  * Copyright(c) 2006-2007, Ext JS, LLC.
5958  *
5959  * Originally Released Under LGPL - original licence link has changed is not relivant.
5960  *
5961  * Fork - LGPL
5962  * <script type="text/javascript">
5963  */
5964
5965 /**
5966  * @class Roo.EventManager
5967  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
5968  * several useful events directly.
5969  * See {@link Roo.EventObject} for more details on normalized event objects.
5970  * @singleton
5971  */
5972 Roo.EventManager = function(){
5973     var docReadyEvent, docReadyProcId, docReadyState = false;
5974     var resizeEvent, resizeTask, textEvent, textSize;
5975     var E = Roo.lib.Event;
5976     var D = Roo.lib.Dom;
5977
5978
5979     var fireDocReady = function(){
5980         if(!docReadyState){
5981             docReadyState = true;
5982             Roo.isReady = true;
5983             if(docReadyProcId){
5984                 clearInterval(docReadyProcId);
5985             }
5986             if(Roo.isGecko || Roo.isOpera) {
5987                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
5988             }
5989             if(Roo.isIE){
5990                 var defer = document.getElementById("ie-deferred-loader");
5991                 if(defer){
5992                     defer.onreadystatechange = null;
5993                     defer.parentNode.removeChild(defer);
5994                 }
5995             }
5996             if(docReadyEvent){
5997                 docReadyEvent.fire();
5998                 docReadyEvent.clearListeners();
5999             }
6000         }
6001     };
6002     
6003     var initDocReady = function(){
6004         docReadyEvent = new Roo.util.Event();
6005         if(Roo.isGecko || Roo.isOpera) {
6006             document.addEventListener("DOMContentLoaded", fireDocReady, false);
6007         }else if(Roo.isIE){
6008             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
6009             var defer = document.getElementById("ie-deferred-loader");
6010             defer.onreadystatechange = function(){
6011                 if(this.readyState == "complete"){
6012                     fireDocReady();
6013                 }
6014             };
6015         }else if(Roo.isSafari){ 
6016             docReadyProcId = setInterval(function(){
6017                 var rs = document.readyState;
6018                 if(rs == "complete") {
6019                     fireDocReady();     
6020                  }
6021             }, 10);
6022         }
6023         // no matter what, make sure it fires on load
6024         E.on(window, "load", fireDocReady);
6025     };
6026
6027     var createBuffered = function(h, o){
6028         var task = new Roo.util.DelayedTask(h);
6029         return function(e){
6030             // create new event object impl so new events don't wipe out properties
6031             e = new Roo.EventObjectImpl(e);
6032             task.delay(o.buffer, h, null, [e]);
6033         };
6034     };
6035
6036     var createSingle = function(h, el, ename, fn){
6037         return function(e){
6038             Roo.EventManager.removeListener(el, ename, fn);
6039             h(e);
6040         };
6041     };
6042
6043     var createDelayed = function(h, o){
6044         return function(e){
6045             // create new event object impl so new events don't wipe out properties
6046             e = new Roo.EventObjectImpl(e);
6047             setTimeout(function(){
6048                 h(e);
6049             }, o.delay || 10);
6050         };
6051     };
6052
6053     var listen = function(element, ename, opt, fn, scope){
6054         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6055         fn = fn || o.fn; scope = scope || o.scope;
6056         var el = Roo.getDom(element);
6057         if(!el){
6058             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6059         }
6060         var h = function(e){
6061             e = Roo.EventObject.setEvent(e);
6062             var t;
6063             if(o.delegate){
6064                 t = e.getTarget(o.delegate, el);
6065                 if(!t){
6066                     return;
6067                 }
6068             }else{
6069                 t = e.target;
6070             }
6071             if(o.stopEvent === true){
6072                 e.stopEvent();
6073             }
6074             if(o.preventDefault === true){
6075                e.preventDefault();
6076             }
6077             if(o.stopPropagation === true){
6078                 e.stopPropagation();
6079             }
6080
6081             if(o.normalized === false){
6082                 e = e.browserEvent;
6083             }
6084
6085             fn.call(scope || el, e, t, o);
6086         };
6087         if(o.delay){
6088             h = createDelayed(h, o);
6089         }
6090         if(o.single){
6091             h = createSingle(h, el, ename, fn);
6092         }
6093         if(o.buffer){
6094             h = createBuffered(h, o);
6095         }
6096         fn._handlers = fn._handlers || [];
6097         fn._handlers.push([Roo.id(el), ename, h]);
6098
6099         E.on(el, ename, h);
6100         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6101             el.addEventListener("DOMMouseScroll", h, false);
6102             E.on(window, 'unload', function(){
6103                 el.removeEventListener("DOMMouseScroll", h, false);
6104             });
6105         }
6106         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6107             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6108         }
6109         return h;
6110     };
6111
6112     var stopListening = function(el, ename, fn){
6113         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6114         if(hds){
6115             for(var i = 0, len = hds.length; i < len; i++){
6116                 var h = hds[i];
6117                 if(h[0] == id && h[1] == ename){
6118                     hd = h[2];
6119                     hds.splice(i, 1);
6120                     break;
6121                 }
6122             }
6123         }
6124         E.un(el, ename, hd);
6125         el = Roo.getDom(el);
6126         if(ename == "mousewheel" && el.addEventListener){
6127             el.removeEventListener("DOMMouseScroll", hd, false);
6128         }
6129         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6130             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6131         }
6132     };
6133
6134     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6135     
6136     var pub = {
6137         
6138         
6139         /** 
6140          * Fix for doc tools
6141          * @scope Roo.EventManager
6142          */
6143         
6144         
6145         /** 
6146          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6147          * object with a Roo.EventObject
6148          * @param {Function} fn        The method the event invokes
6149          * @param {Object}   scope    An object that becomes the scope of the handler
6150          * @param {boolean}  override If true, the obj passed in becomes
6151          *                             the execution scope of the listener
6152          * @return {Function} The wrapped function
6153          * @deprecated
6154          */
6155         wrap : function(fn, scope, override){
6156             return function(e){
6157                 Roo.EventObject.setEvent(e);
6158                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6159             };
6160         },
6161         
6162         /**
6163      * Appends an event handler to an element (shorthand for addListener)
6164      * @param {String/HTMLElement}   element        The html element or id to assign the
6165      * @param {String}   eventName The type of event to listen for
6166      * @param {Function} handler The method the event invokes
6167      * @param {Object}   scope (optional) The scope in which to execute the handler
6168      * function. The handler function's "this" context.
6169      * @param {Object}   options (optional) An object containing handler configuration
6170      * properties. This may contain any of the following properties:<ul>
6171      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6172      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6173      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6174      * <li>preventDefault {Boolean} True to prevent the default action</li>
6175      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6176      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6177      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6178      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6179      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6180      * by the specified number of milliseconds. If the event fires again within that time, the original
6181      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6182      * </ul><br>
6183      * <p>
6184      * <b>Combining Options</b><br>
6185      * Using the options argument, it is possible to combine different types of listeners:<br>
6186      * <br>
6187      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6188      * Code:<pre><code>
6189 el.on('click', this.onClick, this, {
6190     single: true,
6191     delay: 100,
6192     stopEvent : true,
6193     forumId: 4
6194 });</code></pre>
6195      * <p>
6196      * <b>Attaching multiple handlers in 1 call</b><br>
6197       * The method also allows for a single argument to be passed which is a config object containing properties
6198      * which specify multiple handlers.
6199      * <p>
6200      * Code:<pre><code>
6201 el.on({
6202     'click' : {
6203         fn: this.onClick
6204         scope: this,
6205         delay: 100
6206     },
6207     'mouseover' : {
6208         fn: this.onMouseOver
6209         scope: this
6210     },
6211     'mouseout' : {
6212         fn: this.onMouseOut
6213         scope: this
6214     }
6215 });</code></pre>
6216      * <p>
6217      * Or a shorthand syntax:<br>
6218      * Code:<pre><code>
6219 el.on({
6220     'click' : this.onClick,
6221     'mouseover' : this.onMouseOver,
6222     'mouseout' : this.onMouseOut
6223     scope: this
6224 });</code></pre>
6225      */
6226         addListener : function(element, eventName, fn, scope, options){
6227             if(typeof eventName == "object"){
6228                 var o = eventName;
6229                 for(var e in o){
6230                     if(propRe.test(e)){
6231                         continue;
6232                     }
6233                     if(typeof o[e] == "function"){
6234                         // shared options
6235                         listen(element, e, o, o[e], o.scope);
6236                     }else{
6237                         // individual options
6238                         listen(element, e, o[e]);
6239                     }
6240                 }
6241                 return;
6242             }
6243             return listen(element, eventName, options, fn, scope);
6244         },
6245         
6246         /**
6247          * Removes an event handler
6248          *
6249          * @param {String/HTMLElement}   element        The id or html element to remove the 
6250          *                             event from
6251          * @param {String}   eventName     The type of event
6252          * @param {Function} fn
6253          * @return {Boolean} True if a listener was actually removed
6254          */
6255         removeListener : function(element, eventName, fn){
6256             return stopListening(element, eventName, fn);
6257         },
6258         
6259         /**
6260          * Fires when the document is ready (before onload and before images are loaded). Can be 
6261          * accessed shorthanded Roo.onReady().
6262          * @param {Function} fn        The method the event invokes
6263          * @param {Object}   scope    An  object that becomes the scope of the handler
6264          * @param {boolean}  options
6265          */
6266         onDocumentReady : function(fn, scope, options){
6267             if(docReadyState){ // if it already fired
6268                 docReadyEvent.addListener(fn, scope, options);
6269                 docReadyEvent.fire();
6270                 docReadyEvent.clearListeners();
6271                 return;
6272             }
6273             if(!docReadyEvent){
6274                 initDocReady();
6275             }
6276             docReadyEvent.addListener(fn, scope, options);
6277         },
6278         
6279         /**
6280          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6281          * @param {Function} fn        The method the event invokes
6282          * @param {Object}   scope    An object that becomes the scope of the handler
6283          * @param {boolean}  options
6284          */
6285         onWindowResize : function(fn, scope, options){
6286             if(!resizeEvent){
6287                 resizeEvent = new Roo.util.Event();
6288                 resizeTask = new Roo.util.DelayedTask(function(){
6289                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6290                 });
6291                 E.on(window, "resize", function(){
6292                     if(Roo.isIE){
6293                         resizeTask.delay(50);
6294                     }else{
6295                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6296                     }
6297                 });
6298             }
6299             resizeEvent.addListener(fn, scope, options);
6300         },
6301
6302         /**
6303          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6304          * @param {Function} fn        The method the event invokes
6305          * @param {Object}   scope    An object that becomes the scope of the handler
6306          * @param {boolean}  options
6307          */
6308         onTextResize : function(fn, scope, options){
6309             if(!textEvent){
6310                 textEvent = new Roo.util.Event();
6311                 var textEl = new Roo.Element(document.createElement('div'));
6312                 textEl.dom.className = 'x-text-resize';
6313                 textEl.dom.innerHTML = 'X';
6314                 textEl.appendTo(document.body);
6315                 textSize = textEl.dom.offsetHeight;
6316                 setInterval(function(){
6317                     if(textEl.dom.offsetHeight != textSize){
6318                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6319                     }
6320                 }, this.textResizeInterval);
6321             }
6322             textEvent.addListener(fn, scope, options);
6323         },
6324
6325         /**
6326          * Removes the passed window resize listener.
6327          * @param {Function} fn        The method the event invokes
6328          * @param {Object}   scope    The scope of handler
6329          */
6330         removeResizeListener : function(fn, scope){
6331             if(resizeEvent){
6332                 resizeEvent.removeListener(fn, scope);
6333             }
6334         },
6335
6336         // private
6337         fireResize : function(){
6338             if(resizeEvent){
6339                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6340             }   
6341         },
6342         /**
6343          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6344          */
6345         ieDeferSrc : false,
6346         /**
6347          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6348          */
6349         textResizeInterval : 50
6350     };
6351     
6352     /**
6353      * Fix for doc tools
6354      * @scopeAlias pub=Roo.EventManager
6355      */
6356     
6357      /**
6358      * Appends an event handler to an element (shorthand for addListener)
6359      * @param {String/HTMLElement}   element        The html element or id to assign the
6360      * @param {String}   eventName The type of event to listen for
6361      * @param {Function} handler The method the event invokes
6362      * @param {Object}   scope (optional) The scope in which to execute the handler
6363      * function. The handler function's "this" context.
6364      * @param {Object}   options (optional) An object containing handler configuration
6365      * properties. This may contain any of the following properties:<ul>
6366      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6367      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6368      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6369      * <li>preventDefault {Boolean} True to prevent the default action</li>
6370      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6371      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6372      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6373      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6374      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6375      * by the specified number of milliseconds. If the event fires again within that time, the original
6376      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6377      * </ul><br>
6378      * <p>
6379      * <b>Combining Options</b><br>
6380      * Using the options argument, it is possible to combine different types of listeners:<br>
6381      * <br>
6382      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6383      * Code:<pre><code>
6384 el.on('click', this.onClick, this, {
6385     single: true,
6386     delay: 100,
6387     stopEvent : true,
6388     forumId: 4
6389 });</code></pre>
6390      * <p>
6391      * <b>Attaching multiple handlers in 1 call</b><br>
6392       * The method also allows for a single argument to be passed which is a config object containing properties
6393      * which specify multiple handlers.
6394      * <p>
6395      * Code:<pre><code>
6396 el.on({
6397     'click' : {
6398         fn: this.onClick
6399         scope: this,
6400         delay: 100
6401     },
6402     'mouseover' : {
6403         fn: this.onMouseOver
6404         scope: this
6405     },
6406     'mouseout' : {
6407         fn: this.onMouseOut
6408         scope: this
6409     }
6410 });</code></pre>
6411      * <p>
6412      * Or a shorthand syntax:<br>
6413      * Code:<pre><code>
6414 el.on({
6415     'click' : this.onClick,
6416     'mouseover' : this.onMouseOver,
6417     'mouseout' : this.onMouseOut
6418     scope: this
6419 });</code></pre>
6420      */
6421     pub.on = pub.addListener;
6422     pub.un = pub.removeListener;
6423
6424     pub.stoppedMouseDownEvent = new Roo.util.Event();
6425     return pub;
6426 }();
6427 /**
6428   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6429   * @param {Function} fn        The method the event invokes
6430   * @param {Object}   scope    An  object that becomes the scope of the handler
6431   * @param {boolean}  override If true, the obj passed in becomes
6432   *                             the execution scope of the listener
6433   * @member Roo
6434   * @method onReady
6435  */
6436 Roo.onReady = Roo.EventManager.onDocumentReady;
6437
6438 Roo.onReady(function(){
6439     var bd = Roo.get(document.body);
6440     if(!bd){ return; }
6441
6442     var cls = [
6443             Roo.isIE ? "roo-ie"
6444             : Roo.isGecko ? "roo-gecko"
6445             : Roo.isOpera ? "roo-opera"
6446             : Roo.isSafari ? "roo-safari" : ""];
6447
6448     if(Roo.isMac){
6449         cls.push("roo-mac");
6450     }
6451     if(Roo.isLinux){
6452         cls.push("roo-linux");
6453     }
6454     if(Roo.isBorderBox){
6455         cls.push('roo-border-box');
6456     }
6457     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6458         var p = bd.dom.parentNode;
6459         if(p){
6460             p.className += ' roo-strict';
6461         }
6462     }
6463     bd.addClass(cls.join(' '));
6464 });
6465
6466 /**
6467  * @class Roo.EventObject
6468  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6469  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6470  * Example:
6471  * <pre><code>
6472  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6473     e.preventDefault();
6474     var target = e.getTarget();
6475     ...
6476  }
6477  var myDiv = Roo.get("myDiv");
6478  myDiv.on("click", handleClick);
6479  //or
6480  Roo.EventManager.on("myDiv", 'click', handleClick);
6481  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6482  </code></pre>
6483  * @singleton
6484  */
6485 Roo.EventObject = function(){
6486     
6487     var E = Roo.lib.Event;
6488     
6489     // safari keypress events for special keys return bad keycodes
6490     var safariKeys = {
6491         63234 : 37, // left
6492         63235 : 39, // right
6493         63232 : 38, // up
6494         63233 : 40, // down
6495         63276 : 33, // page up
6496         63277 : 34, // page down
6497         63272 : 46, // delete
6498         63273 : 36, // home
6499         63275 : 35  // end
6500     };
6501
6502     // normalize button clicks
6503     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6504                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6505
6506     Roo.EventObjectImpl = function(e){
6507         if(e){
6508             this.setEvent(e.browserEvent || e);
6509         }
6510     };
6511     Roo.EventObjectImpl.prototype = {
6512         /**
6513          * Used to fix doc tools.
6514          * @scope Roo.EventObject.prototype
6515          */
6516             
6517
6518         
6519         
6520         /** The normal browser event */
6521         browserEvent : null,
6522         /** The button pressed in a mouse event */
6523         button : -1,
6524         /** True if the shift key was down during the event */
6525         shiftKey : false,
6526         /** True if the control key was down during the event */
6527         ctrlKey : false,
6528         /** True if the alt key was down during the event */
6529         altKey : false,
6530
6531         /** Key constant 
6532         * @type Number */
6533         BACKSPACE : 8,
6534         /** Key constant 
6535         * @type Number */
6536         TAB : 9,
6537         /** Key constant 
6538         * @type Number */
6539         RETURN : 13,
6540         /** Key constant 
6541         * @type Number */
6542         ENTER : 13,
6543         /** Key constant 
6544         * @type Number */
6545         SHIFT : 16,
6546         /** Key constant 
6547         * @type Number */
6548         CONTROL : 17,
6549         /** Key constant 
6550         * @type Number */
6551         ESC : 27,
6552         /** Key constant 
6553         * @type Number */
6554         SPACE : 32,
6555         /** Key constant 
6556         * @type Number */
6557         PAGEUP : 33,
6558         /** Key constant 
6559         * @type Number */
6560         PAGEDOWN : 34,
6561         /** Key constant 
6562         * @type Number */
6563         END : 35,
6564         /** Key constant 
6565         * @type Number */
6566         HOME : 36,
6567         /** Key constant 
6568         * @type Number */
6569         LEFT : 37,
6570         /** Key constant 
6571         * @type Number */
6572         UP : 38,
6573         /** Key constant 
6574         * @type Number */
6575         RIGHT : 39,
6576         /** Key constant 
6577         * @type Number */
6578         DOWN : 40,
6579         /** Key constant 
6580         * @type Number */
6581         DELETE : 46,
6582         /** Key constant 
6583         * @type Number */
6584         F5 : 116,
6585
6586            /** @private */
6587         setEvent : function(e){
6588             if(e == this || (e && e.browserEvent)){ // already wrapped
6589                 return e;
6590             }
6591             this.browserEvent = e;
6592             if(e){
6593                 // normalize buttons
6594                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6595                 if(e.type == 'click' && this.button == -1){
6596                     this.button = 0;
6597                 }
6598                 this.type = e.type;
6599                 this.shiftKey = e.shiftKey;
6600                 // mac metaKey behaves like ctrlKey
6601                 this.ctrlKey = e.ctrlKey || e.metaKey;
6602                 this.altKey = e.altKey;
6603                 // in getKey these will be normalized for the mac
6604                 this.keyCode = e.keyCode;
6605                 // keyup warnings on firefox.
6606                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6607                 // cache the target for the delayed and or buffered events
6608                 this.target = E.getTarget(e);
6609                 // same for XY
6610                 this.xy = E.getXY(e);
6611             }else{
6612                 this.button = -1;
6613                 this.shiftKey = false;
6614                 this.ctrlKey = false;
6615                 this.altKey = false;
6616                 this.keyCode = 0;
6617                 this.charCode =0;
6618                 this.target = null;
6619                 this.xy = [0, 0];
6620             }
6621             return this;
6622         },
6623
6624         /**
6625          * Stop the event (preventDefault and stopPropagation)
6626          */
6627         stopEvent : function(){
6628             if(this.browserEvent){
6629                 if(this.browserEvent.type == 'mousedown'){
6630                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6631                 }
6632                 E.stopEvent(this.browserEvent);
6633             }
6634         },
6635
6636         /**
6637          * Prevents the browsers default handling of the event.
6638          */
6639         preventDefault : function(){
6640             if(this.browserEvent){
6641                 E.preventDefault(this.browserEvent);
6642             }
6643         },
6644
6645         /** @private */
6646         isNavKeyPress : function(){
6647             var k = this.keyCode;
6648             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6649             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6650         },
6651
6652         isSpecialKey : function(){
6653             var k = this.keyCode;
6654             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6655             (k == 16) || (k == 17) ||
6656             (k >= 18 && k <= 20) ||
6657             (k >= 33 && k <= 35) ||
6658             (k >= 36 && k <= 39) ||
6659             (k >= 44 && k <= 45);
6660         },
6661         /**
6662          * Cancels bubbling of the event.
6663          */
6664         stopPropagation : function(){
6665             if(this.browserEvent){
6666                 if(this.type == 'mousedown'){
6667                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6668                 }
6669                 E.stopPropagation(this.browserEvent);
6670             }
6671         },
6672
6673         /**
6674          * Gets the key code for the event.
6675          * @return {Number}
6676          */
6677         getCharCode : function(){
6678             return this.charCode || this.keyCode;
6679         },
6680
6681         /**
6682          * Returns a normalized keyCode for the event.
6683          * @return {Number} The key code
6684          */
6685         getKey : function(){
6686             var k = this.keyCode || this.charCode;
6687             return Roo.isSafari ? (safariKeys[k] || k) : k;
6688         },
6689
6690         /**
6691          * Gets the x coordinate of the event.
6692          * @return {Number}
6693          */
6694         getPageX : function(){
6695             return this.xy[0];
6696         },
6697
6698         /**
6699          * Gets the y coordinate of the event.
6700          * @return {Number}
6701          */
6702         getPageY : function(){
6703             return this.xy[1];
6704         },
6705
6706         /**
6707          * Gets the time of the event.
6708          * @return {Number}
6709          */
6710         getTime : function(){
6711             if(this.browserEvent){
6712                 return E.getTime(this.browserEvent);
6713             }
6714             return null;
6715         },
6716
6717         /**
6718          * Gets the page coordinates of the event.
6719          * @return {Array} The xy values like [x, y]
6720          */
6721         getXY : function(){
6722             return this.xy;
6723         },
6724
6725         /**
6726          * Gets the target for the event.
6727          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6728          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6729                 search as a number or element (defaults to 10 || document.body)
6730          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6731          * @return {HTMLelement}
6732          */
6733         getTarget : function(selector, maxDepth, returnEl){
6734             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6735         },
6736         /**
6737          * Gets the related target.
6738          * @return {HTMLElement}
6739          */
6740         getRelatedTarget : function(){
6741             if(this.browserEvent){
6742                 return E.getRelatedTarget(this.browserEvent);
6743             }
6744             return null;
6745         },
6746
6747         /**
6748          * Normalizes mouse wheel delta across browsers
6749          * @return {Number} The delta
6750          */
6751         getWheelDelta : function(){
6752             var e = this.browserEvent;
6753             var delta = 0;
6754             if(e.wheelDelta){ /* IE/Opera. */
6755                 delta = e.wheelDelta/120;
6756             }else if(e.detail){ /* Mozilla case. */
6757                 delta = -e.detail/3;
6758             }
6759             return delta;
6760         },
6761
6762         /**
6763          * Returns true if the control, meta, shift or alt key was pressed during this event.
6764          * @return {Boolean}
6765          */
6766         hasModifier : function(){
6767             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6768         },
6769
6770         /**
6771          * Returns true if the target of this event equals el or is a child of el
6772          * @param {String/HTMLElement/Element} el
6773          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6774          * @return {Boolean}
6775          */
6776         within : function(el, related){
6777             var t = this[related ? "getRelatedTarget" : "getTarget"]();
6778             return t && Roo.fly(el).contains(t);
6779         },
6780
6781         getPoint : function(){
6782             return new Roo.lib.Point(this.xy[0], this.xy[1]);
6783         }
6784     };
6785
6786     return new Roo.EventObjectImpl();
6787 }();
6788             
6789     /*
6790  * Based on:
6791  * Ext JS Library 1.1.1
6792  * Copyright(c) 2006-2007, Ext JS, LLC.
6793  *
6794  * Originally Released Under LGPL - original licence link has changed is not relivant.
6795  *
6796  * Fork - LGPL
6797  * <script type="text/javascript">
6798  */
6799
6800  
6801 // was in Composite Element!??!?!
6802  
6803 (function(){
6804     var D = Roo.lib.Dom;
6805     var E = Roo.lib.Event;
6806     var A = Roo.lib.Anim;
6807
6808     // local style camelizing for speed
6809     var propCache = {};
6810     var camelRe = /(-[a-z])/gi;
6811     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
6812     var view = document.defaultView;
6813
6814 /**
6815  * @class Roo.Element
6816  * Represents an Element in the DOM.<br><br>
6817  * Usage:<br>
6818 <pre><code>
6819 var el = Roo.get("my-div");
6820
6821 // or with getEl
6822 var el = getEl("my-div");
6823
6824 // or with a DOM element
6825 var el = Roo.get(myDivElement);
6826 </code></pre>
6827  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
6828  * each call instead of constructing a new one.<br><br>
6829  * <b>Animations</b><br />
6830  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
6831  * should either be a boolean (true) or an object literal with animation options. The animation options are:
6832 <pre>
6833 Option    Default   Description
6834 --------- --------  ---------------------------------------------
6835 duration  .35       The duration of the animation in seconds
6836 easing    easeOut   The YUI easing method
6837 callback  none      A function to execute when the anim completes
6838 scope     this      The scope (this) of the callback function
6839 </pre>
6840 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
6841 * manipulate the animation. Here's an example:
6842 <pre><code>
6843 var el = Roo.get("my-div");
6844
6845 // no animation
6846 el.setWidth(100);
6847
6848 // default animation
6849 el.setWidth(100, true);
6850
6851 // animation with some options set
6852 el.setWidth(100, {
6853     duration: 1,
6854     callback: this.foo,
6855     scope: this
6856 });
6857
6858 // using the "anim" property to get the Anim object
6859 var opt = {
6860     duration: 1,
6861     callback: this.foo,
6862     scope: this
6863 };
6864 el.setWidth(100, opt);
6865 ...
6866 if(opt.anim.isAnimated()){
6867     opt.anim.stop();
6868 }
6869 </code></pre>
6870 * <b> Composite (Collections of) Elements</b><br />
6871  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
6872  * @constructor Create a new Element directly.
6873  * @param {String/HTMLElement} element
6874  * @param {Boolean} forceNew (optional) By default the constructor checks to see if there is already an instance of this element in the cache and if there is it returns the same instance. This will skip that check (useful for extending this class).
6875  */
6876     Roo.Element = function(element, forceNew){
6877         var dom = typeof element == "string" ?
6878                 document.getElementById(element) : element;
6879         if(!dom){ // invalid id/element
6880             return null;
6881         }
6882         var id = dom.id;
6883         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
6884             return Roo.Element.cache[id];
6885         }
6886
6887         /**
6888          * The DOM element
6889          * @type HTMLElement
6890          */
6891         this.dom = dom;
6892
6893         /**
6894          * The DOM element ID
6895          * @type String
6896          */
6897         this.id = id || Roo.id(dom);
6898     };
6899
6900     var El = Roo.Element;
6901
6902     El.prototype = {
6903         /**
6904          * The element's default display mode  (defaults to "")
6905          * @type String
6906          */
6907         originalDisplay : "",
6908
6909         visibilityMode : 1,
6910         /**
6911          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
6912          * @type String
6913          */
6914         defaultUnit : "px",
6915         /**
6916          * Sets the element's visibility mode. When setVisible() is called it
6917          * will use this to determine whether to set the visibility or the display property.
6918          * @param visMode Element.VISIBILITY or Element.DISPLAY
6919          * @return {Roo.Element} this
6920          */
6921         setVisibilityMode : function(visMode){
6922             this.visibilityMode = visMode;
6923             return this;
6924         },
6925         /**
6926          * Convenience method for setVisibilityMode(Element.DISPLAY)
6927          * @param {String} display (optional) What to set display to when visible
6928          * @return {Roo.Element} this
6929          */
6930         enableDisplayMode : function(display){
6931             this.setVisibilityMode(El.DISPLAY);
6932             if(typeof display != "undefined") this.originalDisplay = display;
6933             return this;
6934         },
6935
6936         /**
6937          * Looks at this node and then at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
6938          * @param {String} selector The simple selector to test
6939          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6940                 search as a number or element (defaults to 10 || document.body)
6941          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6942          * @return {HTMLElement} The matching DOM node (or null if no match was found)
6943          */
6944         findParent : function(simpleSelector, maxDepth, returnEl){
6945             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
6946             maxDepth = maxDepth || 50;
6947             if(typeof maxDepth != "number"){
6948                 stopEl = Roo.getDom(maxDepth);
6949                 maxDepth = 10;
6950             }
6951             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
6952                 if(dq.is(p, simpleSelector)){
6953                     return returnEl ? Roo.get(p) : p;
6954                 }
6955                 depth++;
6956                 p = p.parentNode;
6957             }
6958             return null;
6959         },
6960
6961
6962         /**
6963          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
6964          * @param {String} selector The simple selector to test
6965          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6966                 search as a number or element (defaults to 10 || document.body)
6967          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6968          * @return {HTMLElement} The matching DOM node (or null if no match was found)
6969          */
6970         findParentNode : function(simpleSelector, maxDepth, returnEl){
6971             var p = Roo.fly(this.dom.parentNode, '_internal');
6972             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
6973         },
6974
6975         /**
6976          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
6977          * This is a shortcut for findParentNode() that always returns an Roo.Element.
6978          * @param {String} selector The simple selector to test
6979          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6980                 search as a number or element (defaults to 10 || document.body)
6981          * @return {Roo.Element} The matching DOM node (or null if no match was found)
6982          */
6983         up : function(simpleSelector, maxDepth){
6984             return this.findParentNode(simpleSelector, maxDepth, true);
6985         },
6986
6987
6988
6989         /**
6990          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
6991          * @param {String} selector The simple selector to test
6992          * @return {Boolean} True if this element matches the selector, else false
6993          */
6994         is : function(simpleSelector){
6995             return Roo.DomQuery.is(this.dom, simpleSelector);
6996         },
6997
6998         /**
6999          * Perform animation on this element.
7000          * @param {Object} args The YUI animation control args
7001          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
7002          * @param {Function} onComplete (optional) Function to call when animation completes
7003          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
7004          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
7005          * @return {Roo.Element} this
7006          */
7007         animate : function(args, duration, onComplete, easing, animType){
7008             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7009             return this;
7010         },
7011
7012         /*
7013          * @private Internal animation call
7014          */
7015         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7016             animType = animType || 'run';
7017             opt = opt || {};
7018             var anim = Roo.lib.Anim[animType](
7019                 this.dom, args,
7020                 (opt.duration || defaultDur) || .35,
7021                 (opt.easing || defaultEase) || 'easeOut',
7022                 function(){
7023                     Roo.callback(cb, this);
7024                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7025                 },
7026                 this
7027             );
7028             opt.anim = anim;
7029             return anim;
7030         },
7031
7032         // private legacy anim prep
7033         preanim : function(a, i){
7034             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7035         },
7036
7037         /**
7038          * Removes worthless text nodes
7039          * @param {Boolean} forceReclean (optional) By default the element
7040          * keeps track if it has been cleaned already so
7041          * you can call this over and over. However, if you update the element and
7042          * need to force a reclean, you can pass true.
7043          */
7044         clean : function(forceReclean){
7045             if(this.isCleaned && forceReclean !== true){
7046                 return this;
7047             }
7048             var ns = /\S/;
7049             var d = this.dom, n = d.firstChild, ni = -1;
7050             while(n){
7051                 var nx = n.nextSibling;
7052                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7053                     d.removeChild(n);
7054                 }else{
7055                     n.nodeIndex = ++ni;
7056                 }
7057                 n = nx;
7058             }
7059             this.isCleaned = true;
7060             return this;
7061         },
7062
7063         // private
7064         calcOffsetsTo : function(el){
7065             el = Roo.get(el);
7066             var d = el.dom;
7067             var restorePos = false;
7068             if(el.getStyle('position') == 'static'){
7069                 el.position('relative');
7070                 restorePos = true;
7071             }
7072             var x = 0, y =0;
7073             var op = this.dom;
7074             while(op && op != d && op.tagName != 'HTML'){
7075                 x+= op.offsetLeft;
7076                 y+= op.offsetTop;
7077                 op = op.offsetParent;
7078             }
7079             if(restorePos){
7080                 el.position('static');
7081             }
7082             return [x, y];
7083         },
7084
7085         /**
7086          * Scrolls this element into view within the passed container.
7087          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7088          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7089          * @return {Roo.Element} this
7090          */
7091         scrollIntoView : function(container, hscroll){
7092             var c = Roo.getDom(container) || document.body;
7093             var el = this.dom;
7094
7095             var o = this.calcOffsetsTo(c),
7096                 l = o[0],
7097                 t = o[1],
7098                 b = t+el.offsetHeight,
7099                 r = l+el.offsetWidth;
7100
7101             var ch = c.clientHeight;
7102             var ct = parseInt(c.scrollTop, 10);
7103             var cl = parseInt(c.scrollLeft, 10);
7104             var cb = ct + ch;
7105             var cr = cl + c.clientWidth;
7106
7107             if(t < ct){
7108                 c.scrollTop = t;
7109             }else if(b > cb){
7110                 c.scrollTop = b-ch;
7111             }
7112
7113             if(hscroll !== false){
7114                 if(l < cl){
7115                     c.scrollLeft = l;
7116                 }else if(r > cr){
7117                     c.scrollLeft = r-c.clientWidth;
7118                 }
7119             }
7120             return this;
7121         },
7122
7123         // private
7124         scrollChildIntoView : function(child, hscroll){
7125             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7126         },
7127
7128         /**
7129          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7130          * the new height may not be available immediately.
7131          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7132          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7133          * @param {Function} onComplete (optional) Function to call when animation completes
7134          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7135          * @return {Roo.Element} this
7136          */
7137         autoHeight : function(animate, duration, onComplete, easing){
7138             var oldHeight = this.getHeight();
7139             this.clip();
7140             this.setHeight(1); // force clipping
7141             setTimeout(function(){
7142                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7143                 if(!animate){
7144                     this.setHeight(height);
7145                     this.unclip();
7146                     if(typeof onComplete == "function"){
7147                         onComplete();
7148                     }
7149                 }else{
7150                     this.setHeight(oldHeight); // restore original height
7151                     this.setHeight(height, animate, duration, function(){
7152                         this.unclip();
7153                         if(typeof onComplete == "function") onComplete();
7154                     }.createDelegate(this), easing);
7155                 }
7156             }.createDelegate(this), 0);
7157             return this;
7158         },
7159
7160         /**
7161          * Returns true if this element is an ancestor of the passed element
7162          * @param {HTMLElement/String} el The element to check
7163          * @return {Boolean} True if this element is an ancestor of el, else false
7164          */
7165         contains : function(el){
7166             if(!el){return false;}
7167             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7168         },
7169
7170         /**
7171          * Checks whether the element is currently visible using both visibility and display properties.
7172          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7173          * @return {Boolean} True if the element is currently visible, else false
7174          */
7175         isVisible : function(deep) {
7176             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7177             if(deep !== true || !vis){
7178                 return vis;
7179             }
7180             var p = this.dom.parentNode;
7181             while(p && p.tagName.toLowerCase() != "body"){
7182                 if(!Roo.fly(p, '_isVisible').isVisible()){
7183                     return false;
7184                 }
7185                 p = p.parentNode;
7186             }
7187             return true;
7188         },
7189
7190         /**
7191          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7192          * @param {String} selector The CSS selector
7193          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7194          * @return {CompositeElement/CompositeElementLite} The composite element
7195          */
7196         select : function(selector, unique){
7197             return El.select(selector, unique, this.dom);
7198         },
7199
7200         /**
7201          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7202          * @param {String} selector The CSS selector
7203          * @return {Array} An array of the matched nodes
7204          */
7205         query : function(selector, unique){
7206             return Roo.DomQuery.select(selector, this.dom);
7207         },
7208
7209         /**
7210          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7211          * @param {String} selector The CSS selector
7212          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7213          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7214          */
7215         child : function(selector, returnDom){
7216             var n = Roo.DomQuery.selectNode(selector, this.dom);
7217             return returnDom ? n : Roo.get(n);
7218         },
7219
7220         /**
7221          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7222          * @param {String} selector The CSS selector
7223          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7224          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7225          */
7226         down : function(selector, returnDom){
7227             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7228             return returnDom ? n : Roo.get(n);
7229         },
7230
7231         /**
7232          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7233          * @param {String} group The group the DD object is member of
7234          * @param {Object} config The DD config object
7235          * @param {Object} overrides An object containing methods to override/implement on the DD object
7236          * @return {Roo.dd.DD} The DD object
7237          */
7238         initDD : function(group, config, overrides){
7239             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7240             return Roo.apply(dd, overrides);
7241         },
7242
7243         /**
7244          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7245          * @param {String} group The group the DDProxy object is member of
7246          * @param {Object} config The DDProxy config object
7247          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7248          * @return {Roo.dd.DDProxy} The DDProxy object
7249          */
7250         initDDProxy : function(group, config, overrides){
7251             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7252             return Roo.apply(dd, overrides);
7253         },
7254
7255         /**
7256          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7257          * @param {String} group The group the DDTarget object is member of
7258          * @param {Object} config The DDTarget config object
7259          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7260          * @return {Roo.dd.DDTarget} The DDTarget object
7261          */
7262         initDDTarget : function(group, config, overrides){
7263             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7264             return Roo.apply(dd, overrides);
7265         },
7266
7267         /**
7268          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7269          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7270          * @param {Boolean} visible Whether the element is visible
7271          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7272          * @return {Roo.Element} this
7273          */
7274          setVisible : function(visible, animate){
7275             if(!animate || !A){
7276                 if(this.visibilityMode == El.DISPLAY){
7277                     this.setDisplayed(visible);
7278                 }else{
7279                     this.fixDisplay();
7280                     this.dom.style.visibility = visible ? "visible" : "hidden";
7281                 }
7282             }else{
7283                 // closure for composites
7284                 var dom = this.dom;
7285                 var visMode = this.visibilityMode;
7286                 if(visible){
7287                     this.setOpacity(.01);
7288                     this.setVisible(true);
7289                 }
7290                 this.anim({opacity: { to: (visible?1:0) }},
7291                       this.preanim(arguments, 1),
7292                       null, .35, 'easeIn', function(){
7293                          if(!visible){
7294                              if(visMode == El.DISPLAY){
7295                                  dom.style.display = "none";
7296                              }else{
7297                                  dom.style.visibility = "hidden";
7298                              }
7299                              Roo.get(dom).setOpacity(1);
7300                          }
7301                      });
7302             }
7303             return this;
7304         },
7305
7306         /**
7307          * Returns true if display is not "none"
7308          * @return {Boolean}
7309          */
7310         isDisplayed : function() {
7311             return this.getStyle("display") != "none";
7312         },
7313
7314         /**
7315          * Toggles the element's visibility or display, depending on visibility mode.
7316          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7317          * @return {Roo.Element} this
7318          */
7319         toggle : function(animate){
7320             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7321             return this;
7322         },
7323
7324         /**
7325          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7326          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7327          * @return {Roo.Element} this
7328          */
7329         setDisplayed : function(value) {
7330             if(typeof value == "boolean"){
7331                value = value ? this.originalDisplay : "none";
7332             }
7333             this.setStyle("display", value);
7334             return this;
7335         },
7336
7337         /**
7338          * Tries to focus the element. Any exceptions are caught and ignored.
7339          * @return {Roo.Element} this
7340          */
7341         focus : function() {
7342             try{
7343                 this.dom.focus();
7344             }catch(e){}
7345             return this;
7346         },
7347
7348         /**
7349          * Tries to blur the element. Any exceptions are caught and ignored.
7350          * @return {Roo.Element} this
7351          */
7352         blur : function() {
7353             try{
7354                 this.dom.blur();
7355             }catch(e){}
7356             return this;
7357         },
7358
7359         /**
7360          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7361          * @param {String/Array} className The CSS class to add, or an array of classes
7362          * @return {Roo.Element} this
7363          */
7364         addClass : function(className){
7365             if(className instanceof Array){
7366                 for(var i = 0, len = className.length; i < len; i++) {
7367                     this.addClass(className[i]);
7368                 }
7369             }else{
7370                 if(className && !this.hasClass(className)){
7371                     this.dom.className = this.dom.className + " " + className;
7372                 }
7373             }
7374             return this;
7375         },
7376
7377         /**
7378          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7379          * @param {String/Array} className The CSS class to add, or an array of classes
7380          * @return {Roo.Element} this
7381          */
7382         radioClass : function(className){
7383             var siblings = this.dom.parentNode.childNodes;
7384             for(var i = 0; i < siblings.length; i++) {
7385                 var s = siblings[i];
7386                 if(s.nodeType == 1){
7387                     Roo.get(s).removeClass(className);
7388                 }
7389             }
7390             this.addClass(className);
7391             return this;
7392         },
7393
7394         /**
7395          * Removes one or more CSS classes from the element.
7396          * @param {String/Array} className The CSS class to remove, or an array of classes
7397          * @return {Roo.Element} this
7398          */
7399         removeClass : function(className){
7400             if(!className || !this.dom.className){
7401                 return this;
7402             }
7403             if(className instanceof Array){
7404                 for(var i = 0, len = className.length; i < len; i++) {
7405                     this.removeClass(className[i]);
7406                 }
7407             }else{
7408                 if(this.hasClass(className)){
7409                     var re = this.classReCache[className];
7410                     if (!re) {
7411                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7412                        this.classReCache[className] = re;
7413                     }
7414                     this.dom.className =
7415                         this.dom.className.replace(re, " ");
7416                 }
7417             }
7418             return this;
7419         },
7420
7421         // private
7422         classReCache: {},
7423
7424         /**
7425          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7426          * @param {String} className The CSS class to toggle
7427          * @return {Roo.Element} this
7428          */
7429         toggleClass : function(className){
7430             if(this.hasClass(className)){
7431                 this.removeClass(className);
7432             }else{
7433                 this.addClass(className);
7434             }
7435             return this;
7436         },
7437
7438         /**
7439          * Checks if the specified CSS class exists on this element's DOM node.
7440          * @param {String} className The CSS class to check for
7441          * @return {Boolean} True if the class exists, else false
7442          */
7443         hasClass : function(className){
7444             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7445         },
7446
7447         /**
7448          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7449          * @param {String} oldClassName The CSS class to replace
7450          * @param {String} newClassName The replacement CSS class
7451          * @return {Roo.Element} this
7452          */
7453         replaceClass : function(oldClassName, newClassName){
7454             this.removeClass(oldClassName);
7455             this.addClass(newClassName);
7456             return this;
7457         },
7458
7459         /**
7460          * Returns an object with properties matching the styles requested.
7461          * For example, el.getStyles('color', 'font-size', 'width') might return
7462          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7463          * @param {String} style1 A style name
7464          * @param {String} style2 A style name
7465          * @param {String} etc.
7466          * @return {Object} The style object
7467          */
7468         getStyles : function(){
7469             var a = arguments, len = a.length, r = {};
7470             for(var i = 0; i < len; i++){
7471                 r[a[i]] = this.getStyle(a[i]);
7472             }
7473             return r;
7474         },
7475
7476         /**
7477          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7478          * @param {String} property The style property whose value is returned.
7479          * @return {String} The current value of the style property for this element.
7480          */
7481         getStyle : function(){
7482             return view && view.getComputedStyle ?
7483                 function(prop){
7484                     var el = this.dom, v, cs, camel;
7485                     if(prop == 'float'){
7486                         prop = "cssFloat";
7487                     }
7488                     if(el.style && (v = el.style[prop])){
7489                         return v;
7490                     }
7491                     if(cs = view.getComputedStyle(el, "")){
7492                         if(!(camel = propCache[prop])){
7493                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7494                         }
7495                         return cs[camel];
7496                     }
7497                     return null;
7498                 } :
7499                 function(prop){
7500                     var el = this.dom, v, cs, camel;
7501                     if(prop == 'opacity'){
7502                         if(typeof el.style.filter == 'string'){
7503                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7504                             if(m){
7505                                 var fv = parseFloat(m[1]);
7506                                 if(!isNaN(fv)){
7507                                     return fv ? fv / 100 : 0;
7508                                 }
7509                             }
7510                         }
7511                         return 1;
7512                     }else if(prop == 'float'){
7513                         prop = "styleFloat";
7514                     }
7515                     if(!(camel = propCache[prop])){
7516                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7517                     }
7518                     if(v = el.style[camel]){
7519                         return v;
7520                     }
7521                     if(cs = el.currentStyle){
7522                         return cs[camel];
7523                     }
7524                     return null;
7525                 };
7526         }(),
7527
7528         /**
7529          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7530          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7531          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7532          * @return {Roo.Element} this
7533          */
7534         setStyle : function(prop, value){
7535             if(typeof prop == "string"){
7536                 
7537                 if (prop == 'float') {
7538                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7539                     return this;
7540                 }
7541                 
7542                 var camel;
7543                 if(!(camel = propCache[prop])){
7544                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7545                 }
7546                 
7547                 if(camel == 'opacity') {
7548                     this.setOpacity(value);
7549                 }else{
7550                     this.dom.style[camel] = value;
7551                 }
7552             }else{
7553                 for(var style in prop){
7554                     if(typeof prop[style] != "function"){
7555                        this.setStyle(style, prop[style]);
7556                     }
7557                 }
7558             }
7559             return this;
7560         },
7561
7562         /**
7563          * More flexible version of {@link #setStyle} for setting style properties.
7564          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7565          * a function which returns such a specification.
7566          * @return {Roo.Element} this
7567          */
7568         applyStyles : function(style){
7569             Roo.DomHelper.applyStyles(this.dom, style);
7570             return this;
7571         },
7572
7573         /**
7574           * Gets the current X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7575           * @return {Number} The X position of the element
7576           */
7577         getX : function(){
7578             return D.getX(this.dom);
7579         },
7580
7581         /**
7582           * Gets the current Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7583           * @return {Number} The Y position of the element
7584           */
7585         getY : function(){
7586             return D.getY(this.dom);
7587         },
7588
7589         /**
7590           * Gets the current position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7591           * @return {Array} The XY position of the element
7592           */
7593         getXY : function(){
7594             return D.getXY(this.dom);
7595         },
7596
7597         /**
7598          * Sets the X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7599          * @param {Number} The X position of the element
7600          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7601          * @return {Roo.Element} this
7602          */
7603         setX : function(x, animate){
7604             if(!animate || !A){
7605                 D.setX(this.dom, x);
7606             }else{
7607                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7608             }
7609             return this;
7610         },
7611
7612         /**
7613          * Sets the Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7614          * @param {Number} The Y position of the element
7615          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7616          * @return {Roo.Element} this
7617          */
7618         setY : function(y, animate){
7619             if(!animate || !A){
7620                 D.setY(this.dom, y);
7621             }else{
7622                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7623             }
7624             return this;
7625         },
7626
7627         /**
7628          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7629          * @param {String} left The left CSS property value
7630          * @return {Roo.Element} this
7631          */
7632         setLeft : function(left){
7633             this.setStyle("left", this.addUnits(left));
7634             return this;
7635         },
7636
7637         /**
7638          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7639          * @param {String} top The top CSS property value
7640          * @return {Roo.Element} this
7641          */
7642         setTop : function(top){
7643             this.setStyle("top", this.addUnits(top));
7644             return this;
7645         },
7646
7647         /**
7648          * Sets the element's CSS right style.
7649          * @param {String} right The right CSS property value
7650          * @return {Roo.Element} this
7651          */
7652         setRight : function(right){
7653             this.setStyle("right", this.addUnits(right));
7654             return this;
7655         },
7656
7657         /**
7658          * Sets the element's CSS bottom style.
7659          * @param {String} bottom The bottom CSS property value
7660          * @return {Roo.Element} this
7661          */
7662         setBottom : function(bottom){
7663             this.setStyle("bottom", this.addUnits(bottom));
7664             return this;
7665         },
7666
7667         /**
7668          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7669          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7670          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7671          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7672          * @return {Roo.Element} this
7673          */
7674         setXY : function(pos, animate){
7675             if(!animate || !A){
7676                 D.setXY(this.dom, pos);
7677             }else{
7678                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7679             }
7680             return this;
7681         },
7682
7683         /**
7684          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7685          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7686          * @param {Number} x X value for new position (coordinates are page-based)
7687          * @param {Number} y Y value for new position (coordinates are page-based)
7688          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7689          * @return {Roo.Element} this
7690          */
7691         setLocation : function(x, y, animate){
7692             this.setXY([x, y], this.preanim(arguments, 2));
7693             return this;
7694         },
7695
7696         /**
7697          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7698          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7699          * @param {Number} x X value for new position (coordinates are page-based)
7700          * @param {Number} y Y value for new position (coordinates are page-based)
7701          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7702          * @return {Roo.Element} this
7703          */
7704         moveTo : function(x, y, animate){
7705             this.setXY([x, y], this.preanim(arguments, 2));
7706             return this;
7707         },
7708
7709         /**
7710          * Returns the region of the given element.
7711          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7712          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7713          */
7714         getRegion : function(){
7715             return D.getRegion(this.dom);
7716         },
7717
7718         /**
7719          * Returns the offset height of the element
7720          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7721          * @return {Number} The element's height
7722          */
7723         getHeight : function(contentHeight){
7724             var h = this.dom.offsetHeight || 0;
7725             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7726         },
7727
7728         /**
7729          * Returns the offset width of the element
7730          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7731          * @return {Number} The element's width
7732          */
7733         getWidth : function(contentWidth){
7734             var w = this.dom.offsetWidth || 0;
7735             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7736         },
7737
7738         /**
7739          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7740          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7741          * if a height has not been set using CSS.
7742          * @return {Number}
7743          */
7744         getComputedHeight : function(){
7745             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7746             if(!h){
7747                 h = parseInt(this.getStyle('height'), 10) || 0;
7748                 if(!this.isBorderBox()){
7749                     h += this.getFrameWidth('tb');
7750                 }
7751             }
7752             return h;
7753         },
7754
7755         /**
7756          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7757          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7758          * if a width has not been set using CSS.
7759          * @return {Number}
7760          */
7761         getComputedWidth : function(){
7762             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
7763             if(!w){
7764                 w = parseInt(this.getStyle('width'), 10) || 0;
7765                 if(!this.isBorderBox()){
7766                     w += this.getFrameWidth('lr');
7767                 }
7768             }
7769             return w;
7770         },
7771
7772         /**
7773          * Returns the size of the element.
7774          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
7775          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
7776          */
7777         getSize : function(contentSize){
7778             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
7779         },
7780
7781         /**
7782          * Returns the width and height of the viewport.
7783          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
7784          */
7785         getViewSize : function(){
7786             var d = this.dom, doc = document, aw = 0, ah = 0;
7787             if(d == doc || d == doc.body){
7788                 return {width : D.getViewWidth(), height: D.getViewHeight()};
7789             }else{
7790                 return {
7791                     width : d.clientWidth,
7792                     height: d.clientHeight
7793                 };
7794             }
7795         },
7796
7797         /**
7798          * Returns the value of the "value" attribute
7799          * @param {Boolean} asNumber true to parse the value as a number
7800          * @return {String/Number}
7801          */
7802         getValue : function(asNumber){
7803             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
7804         },
7805
7806         // private
7807         adjustWidth : function(width){
7808             if(typeof width == "number"){
7809                 if(this.autoBoxAdjust && !this.isBorderBox()){
7810                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
7811                 }
7812                 if(width < 0){
7813                     width = 0;
7814                 }
7815             }
7816             return width;
7817         },
7818
7819         // private
7820         adjustHeight : function(height){
7821             if(typeof height == "number"){
7822                if(this.autoBoxAdjust && !this.isBorderBox()){
7823                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
7824                }
7825                if(height < 0){
7826                    height = 0;
7827                }
7828             }
7829             return height;
7830         },
7831
7832         /**
7833          * Set the width of the element
7834          * @param {Number} width The new width
7835          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7836          * @return {Roo.Element} this
7837          */
7838         setWidth : function(width, animate){
7839             width = this.adjustWidth(width);
7840             if(!animate || !A){
7841                 this.dom.style.width = this.addUnits(width);
7842             }else{
7843                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
7844             }
7845             return this;
7846         },
7847
7848         /**
7849          * Set the height of the element
7850          * @param {Number} height The new height
7851          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7852          * @return {Roo.Element} this
7853          */
7854          setHeight : function(height, animate){
7855             height = this.adjustHeight(height);
7856             if(!animate || !A){
7857                 this.dom.style.height = this.addUnits(height);
7858             }else{
7859                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
7860             }
7861             return this;
7862         },
7863
7864         /**
7865          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
7866          * @param {Number} width The new width
7867          * @param {Number} height The new height
7868          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7869          * @return {Roo.Element} this
7870          */
7871          setSize : function(width, height, animate){
7872             if(typeof width == "object"){ // in case of object from getSize()
7873                 height = width.height; width = width.width;
7874             }
7875             width = this.adjustWidth(width); height = this.adjustHeight(height);
7876             if(!animate || !A){
7877                 this.dom.style.width = this.addUnits(width);
7878                 this.dom.style.height = this.addUnits(height);
7879             }else{
7880                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
7881             }
7882             return this;
7883         },
7884
7885         /**
7886          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
7887          * @param {Number} x X value for new position (coordinates are page-based)
7888          * @param {Number} y Y value for new position (coordinates are page-based)
7889          * @param {Number} width The new width
7890          * @param {Number} height The new height
7891          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7892          * @return {Roo.Element} this
7893          */
7894         setBounds : function(x, y, width, height, animate){
7895             if(!animate || !A){
7896                 this.setSize(width, height);
7897                 this.setLocation(x, y);
7898             }else{
7899                 width = this.adjustWidth(width); height = this.adjustHeight(height);
7900                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
7901                               this.preanim(arguments, 4), 'motion');
7902             }
7903             return this;
7904         },
7905
7906         /**
7907          * Sets the element's position and size the the specified region. If animation is true then width, height, x and y will be animated concurrently.
7908          * @param {Roo.lib.Region} region The region to fill
7909          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7910          * @return {Roo.Element} this
7911          */
7912         setRegion : function(region, animate){
7913             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
7914             return this;
7915         },
7916
7917         /**
7918          * Appends an event handler
7919          *
7920          * @param {String}   eventName     The type of event to append
7921          * @param {Function} fn        The method the event invokes
7922          * @param {Object} scope       (optional) The scope (this object) of the fn
7923          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
7924          */
7925         addListener : function(eventName, fn, scope, options){
7926             if (this.dom) {
7927                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
7928             }
7929         },
7930
7931         /**
7932          * Removes an event handler from this element
7933          * @param {String} eventName the type of event to remove
7934          * @param {Function} fn the method the event invokes
7935          * @return {Roo.Element} this
7936          */
7937         removeListener : function(eventName, fn){
7938             Roo.EventManager.removeListener(this.dom,  eventName, fn);
7939             return this;
7940         },
7941
7942         /**
7943          * Removes all previous added listeners from this element
7944          * @return {Roo.Element} this
7945          */
7946         removeAllListeners : function(){
7947             E.purgeElement(this.dom);
7948             return this;
7949         },
7950
7951         relayEvent : function(eventName, observable){
7952             this.on(eventName, function(e){
7953                 observable.fireEvent(eventName, e);
7954             });
7955         },
7956
7957         /**
7958          * Set the opacity of the element
7959          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
7960          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7961          * @return {Roo.Element} this
7962          */
7963          setOpacity : function(opacity, animate){
7964             if(!animate || !A){
7965                 var s = this.dom.style;
7966                 if(Roo.isIE){
7967                     s.zoom = 1;
7968                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
7969                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
7970                 }else{
7971                     s.opacity = opacity;
7972                 }
7973             }else{
7974                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
7975             }
7976             return this;
7977         },
7978
7979         /**
7980          * Gets the left X coordinate
7981          * @param {Boolean} local True to get the local css position instead of page coordinate
7982          * @return {Number}
7983          */
7984         getLeft : function(local){
7985             if(!local){
7986                 return this.getX();
7987             }else{
7988                 return parseInt(this.getStyle("left"), 10) || 0;
7989             }
7990         },
7991
7992         /**
7993          * Gets the right X coordinate of the element (element X position + element width)
7994          * @param {Boolean} local True to get the local css position instead of page coordinate
7995          * @return {Number}
7996          */
7997         getRight : function(local){
7998             if(!local){
7999                 return this.getX() + this.getWidth();
8000             }else{
8001                 return (this.getLeft(true) + this.getWidth()) || 0;
8002             }
8003         },
8004
8005         /**
8006          * Gets the top Y coordinate
8007          * @param {Boolean} local True to get the local css position instead of page coordinate
8008          * @return {Number}
8009          */
8010         getTop : function(local) {
8011             if(!local){
8012                 return this.getY();
8013             }else{
8014                 return parseInt(this.getStyle("top"), 10) || 0;
8015             }
8016         },
8017
8018         /**
8019          * Gets the bottom Y coordinate of the element (element Y position + element height)
8020          * @param {Boolean} local True to get the local css position instead of page coordinate
8021          * @return {Number}
8022          */
8023         getBottom : function(local){
8024             if(!local){
8025                 return this.getY() + this.getHeight();
8026             }else{
8027                 return (this.getTop(true) + this.getHeight()) || 0;
8028             }
8029         },
8030
8031         /**
8032         * Initializes positioning on this element. If a desired position is not passed, it will make the
8033         * the element positioned relative IF it is not already positioned.
8034         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8035         * @param {Number} zIndex (optional) The zIndex to apply
8036         * @param {Number} x (optional) Set the page X position
8037         * @param {Number} y (optional) Set the page Y position
8038         */
8039         position : function(pos, zIndex, x, y){
8040             if(!pos){
8041                if(this.getStyle('position') == 'static'){
8042                    this.setStyle('position', 'relative');
8043                }
8044             }else{
8045                 this.setStyle("position", pos);
8046             }
8047             if(zIndex){
8048                 this.setStyle("z-index", zIndex);
8049             }
8050             if(x !== undefined && y !== undefined){
8051                 this.setXY([x, y]);
8052             }else if(x !== undefined){
8053                 this.setX(x);
8054             }else if(y !== undefined){
8055                 this.setY(y);
8056             }
8057         },
8058
8059         /**
8060         * Clear positioning back to the default when the document was loaded
8061         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8062         * @return {Roo.Element} this
8063          */
8064         clearPositioning : function(value){
8065             value = value ||'';
8066             this.setStyle({
8067                 "left": value,
8068                 "right": value,
8069                 "top": value,
8070                 "bottom": value,
8071                 "z-index": "",
8072                 "position" : "static"
8073             });
8074             return this;
8075         },
8076
8077         /**
8078         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8079         * snapshot before performing an update and then restoring the element.
8080         * @return {Object}
8081         */
8082         getPositioning : function(){
8083             var l = this.getStyle("left");
8084             var t = this.getStyle("top");
8085             return {
8086                 "position" : this.getStyle("position"),
8087                 "left" : l,
8088                 "right" : l ? "" : this.getStyle("right"),
8089                 "top" : t,
8090                 "bottom" : t ? "" : this.getStyle("bottom"),
8091                 "z-index" : this.getStyle("z-index")
8092             };
8093         },
8094
8095         /**
8096          * Gets the width of the border(s) for the specified side(s)
8097          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8098          * passing lr would get the border (l)eft width + the border (r)ight width.
8099          * @return {Number} The width of the sides passed added together
8100          */
8101         getBorderWidth : function(side){
8102             return this.addStyles(side, El.borders);
8103         },
8104
8105         /**
8106          * Gets the width of the padding(s) for the specified side(s)
8107          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8108          * passing lr would get the padding (l)eft + the padding (r)ight.
8109          * @return {Number} The padding of the sides passed added together
8110          */
8111         getPadding : function(side){
8112             return this.addStyles(side, El.paddings);
8113         },
8114
8115         /**
8116         * Set positioning with an object returned by getPositioning().
8117         * @param {Object} posCfg
8118         * @return {Roo.Element} this
8119          */
8120         setPositioning : function(pc){
8121             this.applyStyles(pc);
8122             if(pc.right == "auto"){
8123                 this.dom.style.right = "";
8124             }
8125             if(pc.bottom == "auto"){
8126                 this.dom.style.bottom = "";
8127             }
8128             return this;
8129         },
8130
8131         // private
8132         fixDisplay : function(){
8133             if(this.getStyle("display") == "none"){
8134                 this.setStyle("visibility", "hidden");
8135                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8136                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8137                     this.setStyle("display", "block");
8138                 }
8139             }
8140         },
8141
8142         /**
8143          * Quick set left and top adding default units
8144          * @param {String} left The left CSS property value
8145          * @param {String} top The top CSS property value
8146          * @return {Roo.Element} this
8147          */
8148          setLeftTop : function(left, top){
8149             this.dom.style.left = this.addUnits(left);
8150             this.dom.style.top = this.addUnits(top);
8151             return this;
8152         },
8153
8154         /**
8155          * Move this element relative to its current position.
8156          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8157          * @param {Number} distance How far to move the element in pixels
8158          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8159          * @return {Roo.Element} this
8160          */
8161          move : function(direction, distance, animate){
8162             var xy = this.getXY();
8163             direction = direction.toLowerCase();
8164             switch(direction){
8165                 case "l":
8166                 case "left":
8167                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8168                     break;
8169                case "r":
8170                case "right":
8171                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8172                     break;
8173                case "t":
8174                case "top":
8175                case "up":
8176                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8177                     break;
8178                case "b":
8179                case "bottom":
8180                case "down":
8181                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8182                     break;
8183             }
8184             return this;
8185         },
8186
8187         /**
8188          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8189          * @return {Roo.Element} this
8190          */
8191         clip : function(){
8192             if(!this.isClipped){
8193                this.isClipped = true;
8194                this.originalClip = {
8195                    "o": this.getStyle("overflow"),
8196                    "x": this.getStyle("overflow-x"),
8197                    "y": this.getStyle("overflow-y")
8198                };
8199                this.setStyle("overflow", "hidden");
8200                this.setStyle("overflow-x", "hidden");
8201                this.setStyle("overflow-y", "hidden");
8202             }
8203             return this;
8204         },
8205
8206         /**
8207          *  Return clipping (overflow) to original clipping before clip() was called
8208          * @return {Roo.Element} this
8209          */
8210         unclip : function(){
8211             if(this.isClipped){
8212                 this.isClipped = false;
8213                 var o = this.originalClip;
8214                 if(o.o){this.setStyle("overflow", o.o);}
8215                 if(o.x){this.setStyle("overflow-x", o.x);}
8216                 if(o.y){this.setStyle("overflow-y", o.y);}
8217             }
8218             return this;
8219         },
8220
8221
8222         /**
8223          * Gets the x,y coordinates specified by the anchor position on the element.
8224          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8225          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8226          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8227          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8228          * @return {Array} [x, y] An array containing the element's x and y coordinates
8229          */
8230         getAnchorXY : function(anchor, local, s){
8231             //Passing a different size is useful for pre-calculating anchors,
8232             //especially for anchored animations that change the el size.
8233
8234             var w, h, vp = false;
8235             if(!s){
8236                 var d = this.dom;
8237                 if(d == document.body || d == document){
8238                     vp = true;
8239                     w = D.getViewWidth(); h = D.getViewHeight();
8240                 }else{
8241                     w = this.getWidth(); h = this.getHeight();
8242                 }
8243             }else{
8244                 w = s.width;  h = s.height;
8245             }
8246             var x = 0, y = 0, r = Math.round;
8247             switch((anchor || "tl").toLowerCase()){
8248                 case "c":
8249                     x = r(w*.5);
8250                     y = r(h*.5);
8251                 break;
8252                 case "t":
8253                     x = r(w*.5);
8254                     y = 0;
8255                 break;
8256                 case "l":
8257                     x = 0;
8258                     y = r(h*.5);
8259                 break;
8260                 case "r":
8261                     x = w;
8262                     y = r(h*.5);
8263                 break;
8264                 case "b":
8265                     x = r(w*.5);
8266                     y = h;
8267                 break;
8268                 case "tl":
8269                     x = 0;
8270                     y = 0;
8271                 break;
8272                 case "bl":
8273                     x = 0;
8274                     y = h;
8275                 break;
8276                 case "br":
8277                     x = w;
8278                     y = h;
8279                 break;
8280                 case "tr":
8281                     x = w;
8282                     y = 0;
8283                 break;
8284             }
8285             if(local === true){
8286                 return [x, y];
8287             }
8288             if(vp){
8289                 var sc = this.getScroll();
8290                 return [x + sc.left, y + sc.top];
8291             }
8292             //Add the element's offset xy
8293             var o = this.getXY();
8294             return [x+o[0], y+o[1]];
8295         },
8296
8297         /**
8298          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8299          * supported position values.
8300          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8301          * @param {String} position The position to align to.
8302          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8303          * @return {Array} [x, y]
8304          */
8305         getAlignToXY : function(el, p, o){
8306             el = Roo.get(el);
8307             var d = this.dom;
8308             if(!el.dom){
8309                 throw "Element.alignTo with an element that doesn't exist";
8310             }
8311             var c = false; //constrain to viewport
8312             var p1 = "", p2 = "";
8313             o = o || [0,0];
8314
8315             if(!p){
8316                 p = "tl-bl";
8317             }else if(p == "?"){
8318                 p = "tl-bl?";
8319             }else if(p.indexOf("-") == -1){
8320                 p = "tl-" + p;
8321             }
8322             p = p.toLowerCase();
8323             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8324             if(!m){
8325                throw "Element.alignTo with an invalid alignment " + p;
8326             }
8327             p1 = m[1]; p2 = m[2]; c = !!m[3];
8328
8329             //Subtract the aligned el's internal xy from the target's offset xy
8330             //plus custom offset to get the aligned el's new offset xy
8331             var a1 = this.getAnchorXY(p1, true);
8332             var a2 = el.getAnchorXY(p2, false);
8333             var x = a2[0] - a1[0] + o[0];
8334             var y = a2[1] - a1[1] + o[1];
8335             if(c){
8336                 //constrain the aligned el to viewport if necessary
8337                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8338                 // 5px of margin for ie
8339                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8340
8341                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8342                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8343                 //otherwise swap the aligned el to the opposite border of the target.
8344                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8345                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8346                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8347                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8348
8349                var doc = document;
8350                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8351                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8352
8353                if((x+w) > dw + scrollX){
8354                     x = swapX ? r.left-w : dw+scrollX-w;
8355                 }
8356                if(x < scrollX){
8357                    x = swapX ? r.right : scrollX;
8358                }
8359                if((y+h) > dh + scrollY){
8360                     y = swapY ? r.top-h : dh+scrollY-h;
8361                 }
8362                if (y < scrollY){
8363                    y = swapY ? r.bottom : scrollY;
8364                }
8365             }
8366             return [x,y];
8367         },
8368
8369         // private
8370         getConstrainToXY : function(){
8371             var os = {top:0, left:0, bottom:0, right: 0};
8372
8373             return function(el, local, offsets, proposedXY){
8374                 el = Roo.get(el);
8375                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8376
8377                 var vw, vh, vx = 0, vy = 0;
8378                 if(el.dom == document.body || el.dom == document){
8379                     vw = Roo.lib.Dom.getViewWidth();
8380                     vh = Roo.lib.Dom.getViewHeight();
8381                 }else{
8382                     vw = el.dom.clientWidth;
8383                     vh = el.dom.clientHeight;
8384                     if(!local){
8385                         var vxy = el.getXY();
8386                         vx = vxy[0];
8387                         vy = vxy[1];
8388                     }
8389                 }
8390
8391                 var s = el.getScroll();
8392
8393                 vx += offsets.left + s.left;
8394                 vy += offsets.top + s.top;
8395
8396                 vw -= offsets.right;
8397                 vh -= offsets.bottom;
8398
8399                 var vr = vx+vw;
8400                 var vb = vy+vh;
8401
8402                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8403                 var x = xy[0], y = xy[1];
8404                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8405
8406                 // only move it if it needs it
8407                 var moved = false;
8408
8409                 // first validate right/bottom
8410                 if((x + w) > vr){
8411                     x = vr - w;
8412                     moved = true;
8413                 }
8414                 if((y + h) > vb){
8415                     y = vb - h;
8416                     moved = true;
8417                 }
8418                 // then make sure top/left isn't negative
8419                 if(x < vx){
8420                     x = vx;
8421                     moved = true;
8422                 }
8423                 if(y < vy){
8424                     y = vy;
8425                     moved = true;
8426                 }
8427                 return moved ? [x, y] : false;
8428             };
8429         }(),
8430
8431         // private
8432         adjustForConstraints : function(xy, parent, offsets){
8433             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8434         },
8435
8436         /**
8437          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8438          * document it aligns it to the viewport.
8439          * The position parameter is optional, and can be specified in any one of the following formats:
8440          * <ul>
8441          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8442          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8443          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8444          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8445          *   <li><b>Two anchors</b>: If two values from the table below are passed separated by a dash, the first value is used as the
8446          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8447          * </ul>
8448          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8449          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8450          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8451          * that specified in order to enforce the viewport constraints.
8452          * Following are all of the supported anchor positions:
8453     <pre>
8454     Value  Description
8455     -----  -----------------------------
8456     tl     The top left corner (default)
8457     t      The center of the top edge
8458     tr     The top right corner
8459     l      The center of the left edge
8460     c      In the center of the element
8461     r      The center of the right edge
8462     bl     The bottom left corner
8463     b      The center of the bottom edge
8464     br     The bottom right corner
8465     </pre>
8466     Example Usage:
8467     <pre><code>
8468     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8469     el.alignTo("other-el");
8470
8471     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8472     el.alignTo("other-el", "tr?");
8473
8474     // align the bottom right corner of el with the center left edge of other-el
8475     el.alignTo("other-el", "br-l?");
8476
8477     // align the center of el with the bottom left corner of other-el and
8478     // adjust the x position by -6 pixels (and the y position by 0)
8479     el.alignTo("other-el", "c-bl", [-6, 0]);
8480     </code></pre>
8481          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8482          * @param {String} position The position to align to.
8483          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8484          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8485          * @return {Roo.Element} this
8486          */
8487         alignTo : function(element, position, offsets, animate){
8488             var xy = this.getAlignToXY(element, position, offsets);
8489             this.setXY(xy, this.preanim(arguments, 3));
8490             return this;
8491         },
8492
8493         /**
8494          * Anchors an element to another element and realigns it when the window is resized.
8495          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8496          * @param {String} position The position to align to.
8497          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8498          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8499          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8500          * is a number, it is used as the buffer delay (defaults to 50ms).
8501          * @param {Function} callback The function to call after the animation finishes
8502          * @return {Roo.Element} this
8503          */
8504         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8505             var action = function(){
8506                 this.alignTo(el, alignment, offsets, animate);
8507                 Roo.callback(callback, this);
8508             };
8509             Roo.EventManager.onWindowResize(action, this);
8510             var tm = typeof monitorScroll;
8511             if(tm != 'undefined'){
8512                 Roo.EventManager.on(window, 'scroll', action, this,
8513                     {buffer: tm == 'number' ? monitorScroll : 50});
8514             }
8515             action.call(this); // align immediately
8516             return this;
8517         },
8518         /**
8519          * Clears any opacity settings from this element. Required in some cases for IE.
8520          * @return {Roo.Element} this
8521          */
8522         clearOpacity : function(){
8523             if (window.ActiveXObject) {
8524                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8525                     this.dom.style.filter = "";
8526                 }
8527             } else {
8528                 this.dom.style.opacity = "";
8529                 this.dom.style["-moz-opacity"] = "";
8530                 this.dom.style["-khtml-opacity"] = "";
8531             }
8532             return this;
8533         },
8534
8535         /**
8536          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8537          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8538          * @return {Roo.Element} this
8539          */
8540         hide : function(animate){
8541             this.setVisible(false, this.preanim(arguments, 0));
8542             return this;
8543         },
8544
8545         /**
8546         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8547         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8548          * @return {Roo.Element} this
8549          */
8550         show : function(animate){
8551             this.setVisible(true, this.preanim(arguments, 0));
8552             return this;
8553         },
8554
8555         /**
8556          * @private Test if size has a unit, otherwise appends the default
8557          */
8558         addUnits : function(size){
8559             return Roo.Element.addUnits(size, this.defaultUnit);
8560         },
8561
8562         /**
8563          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8564          * @return {Roo.Element} this
8565          */
8566         beginMeasure : function(){
8567             var el = this.dom;
8568             if(el.offsetWidth || el.offsetHeight){
8569                 return this; // offsets work already
8570             }
8571             var changed = [];
8572             var p = this.dom, b = document.body; // start with this element
8573             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8574                 var pe = Roo.get(p);
8575                 if(pe.getStyle('display') == 'none'){
8576                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8577                     p.style.visibility = "hidden";
8578                     p.style.display = "block";
8579                 }
8580                 p = p.parentNode;
8581             }
8582             this._measureChanged = changed;
8583             return this;
8584
8585         },
8586
8587         /**
8588          * Restores displays to before beginMeasure was called
8589          * @return {Roo.Element} this
8590          */
8591         endMeasure : function(){
8592             var changed = this._measureChanged;
8593             if(changed){
8594                 for(var i = 0, len = changed.length; i < len; i++) {
8595                     var r = changed[i];
8596                     r.el.style.visibility = r.visibility;
8597                     r.el.style.display = "none";
8598                 }
8599                 this._measureChanged = null;
8600             }
8601             return this;
8602         },
8603
8604         /**
8605         * Update the innerHTML of this element, optionally searching for and processing scripts
8606         * @param {String} html The new HTML
8607         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8608         * @param {Function} callback For async script loading you can be noticed when the update completes
8609         * @return {Roo.Element} this
8610          */
8611         update : function(html, loadScripts, callback){
8612             if(typeof html == "undefined"){
8613                 html = "";
8614             }
8615             if(loadScripts !== true){
8616                 this.dom.innerHTML = html;
8617                 if(typeof callback == "function"){
8618                     callback();
8619                 }
8620                 return this;
8621             }
8622             var id = Roo.id();
8623             var dom = this.dom;
8624
8625             html += '<span id="' + id + '"></span>';
8626
8627             E.onAvailable(id, function(){
8628                 var hd = document.getElementsByTagName("head")[0];
8629                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8630                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8631                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8632
8633                 var match;
8634                 while(match = re.exec(html)){
8635                     var attrs = match[1];
8636                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8637                     if(srcMatch && srcMatch[2]){
8638                        var s = document.createElement("script");
8639                        s.src = srcMatch[2];
8640                        var typeMatch = attrs.match(typeRe);
8641                        if(typeMatch && typeMatch[2]){
8642                            s.type = typeMatch[2];
8643                        }
8644                        hd.appendChild(s);
8645                     }else if(match[2] && match[2].length > 0){
8646                         if(window.execScript) {
8647                            window.execScript(match[2]);
8648                         } else {
8649                             /**
8650                              * eval:var:id
8651                              * eval:var:dom
8652                              * eval:var:html
8653                              * 
8654                              */
8655                            window.eval(match[2]);
8656                         }
8657                     }
8658                 }
8659                 var el = document.getElementById(id);
8660                 if(el){el.parentNode.removeChild(el);}
8661                 if(typeof callback == "function"){
8662                     callback();
8663                 }
8664             });
8665             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8666             return this;
8667         },
8668
8669         /**
8670          * Direct access to the UpdateManager update() method (takes the same parameters).
8671          * @param {String/Function} url The url for this request or a function to call to get the url
8672          * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
8673          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8674          * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used url. If true, it will not store the url.
8675          * @return {Roo.Element} this
8676          */
8677         load : function(){
8678             var um = this.getUpdateManager();
8679             um.update.apply(um, arguments);
8680             return this;
8681         },
8682
8683         /**
8684         * Gets this element's UpdateManager
8685         * @return {Roo.UpdateManager} The UpdateManager
8686         */
8687         getUpdateManager : function(){
8688             if(!this.updateManager){
8689                 this.updateManager = new Roo.UpdateManager(this);
8690             }
8691             return this.updateManager;
8692         },
8693
8694         /**
8695          * Disables text selection for this element (normalized across browsers)
8696          * @return {Roo.Element} this
8697          */
8698         unselectable : function(){
8699             this.dom.unselectable = "on";
8700             this.swallowEvent("selectstart", true);
8701             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8702             this.addClass("x-unselectable");
8703             return this;
8704         },
8705
8706         /**
8707         * Calculates the x, y to center this element on the screen
8708         * @return {Array} The x, y values [x, y]
8709         */
8710         getCenterXY : function(){
8711             return this.getAlignToXY(document, 'c-c');
8712         },
8713
8714         /**
8715         * Centers the Element in either the viewport, or another Element.
8716         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8717         */
8718         center : function(centerIn){
8719             this.alignTo(centerIn || document, 'c-c');
8720             return this;
8721         },
8722
8723         /**
8724          * Tests various css rules/browsers to determine if this element uses a border box
8725          * @return {Boolean}
8726          */
8727         isBorderBox : function(){
8728             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8729         },
8730
8731         /**
8732          * Return a box {x, y, width, height} that can be used to set another elements
8733          * size/location to match this element.
8734          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8735          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8736          * @return {Object} box An object in the format {x, y, width, height}
8737          */
8738         getBox : function(contentBox, local){
8739             var xy;
8740             if(!local){
8741                 xy = this.getXY();
8742             }else{
8743                 var left = parseInt(this.getStyle("left"), 10) || 0;
8744                 var top = parseInt(this.getStyle("top"), 10) || 0;
8745                 xy = [left, top];
8746             }
8747             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8748             if(!contentBox){
8749                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8750             }else{
8751                 var l = this.getBorderWidth("l")+this.getPadding("l");
8752                 var r = this.getBorderWidth("r")+this.getPadding("r");
8753                 var t = this.getBorderWidth("t")+this.getPadding("t");
8754                 var b = this.getBorderWidth("b")+this.getPadding("b");
8755                 bx = {x: xy[0]+l, y: xy[1]+t, 0: xy[0]+l, 1: xy[1]+t, width: w-(l+r), height: h-(t+b)};
8756             }
8757             bx.right = bx.x + bx.width;
8758             bx.bottom = bx.y + bx.height;
8759             return bx;
8760         },
8761
8762         /**
8763          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
8764          for more information about the sides.
8765          * @param {String} sides
8766          * @return {Number}
8767          */
8768         getFrameWidth : function(sides, onlyContentBox){
8769             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
8770         },
8771
8772         /**
8773          * Sets the element's box. Use getBox() on another element to get a box obj. If animate is true then width, height, x and y will be animated concurrently.
8774          * @param {Object} box The box to fill {x, y, width, height}
8775          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
8776          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8777          * @return {Roo.Element} this
8778          */
8779         setBox : function(box, adjust, animate){
8780             var w = box.width, h = box.height;
8781             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
8782                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8783                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8784             }
8785             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
8786             return this;
8787         },
8788
8789         /**
8790          * Forces the browser to repaint this element
8791          * @return {Roo.Element} this
8792          */
8793          repaint : function(){
8794             var dom = this.dom;
8795             this.addClass("x-repaint");
8796             setTimeout(function(){
8797                 Roo.get(dom).removeClass("x-repaint");
8798             }, 1);
8799             return this;
8800         },
8801
8802         /**
8803          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
8804          * then it returns the calculated width of the sides (see getPadding)
8805          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
8806          * @return {Object/Number}
8807          */
8808         getMargins : function(side){
8809             if(!side){
8810                 return {
8811                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
8812                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
8813                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
8814                     right: parseInt(this.getStyle("margin-right"), 10) || 0
8815                 };
8816             }else{
8817                 return this.addStyles(side, El.margins);
8818              }
8819         },
8820
8821         // private
8822         addStyles : function(sides, styles){
8823             var val = 0, v, w;
8824             for(var i = 0, len = sides.length; i < len; i++){
8825                 v = this.getStyle(styles[sides.charAt(i)]);
8826                 if(v){
8827                      w = parseInt(v, 10);
8828                      if(w){ val += w; }
8829                 }
8830             }
8831             return val;
8832         },
8833
8834         /**
8835          * Creates a proxy element of this element
8836          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
8837          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
8838          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
8839          * @return {Roo.Element} The new proxy element
8840          */
8841         createProxy : function(config, renderTo, matchBox){
8842             if(renderTo){
8843                 renderTo = Roo.getDom(renderTo);
8844             }else{
8845                 renderTo = document.body;
8846             }
8847             config = typeof config == "object" ?
8848                 config : {tag : "div", cls: config};
8849             var proxy = Roo.DomHelper.append(renderTo, config, true);
8850             if(matchBox){
8851                proxy.setBox(this.getBox());
8852             }
8853             return proxy;
8854         },
8855
8856         /**
8857          * Puts a mask over this element to disable user interaction. Requires core.css.
8858          * This method can only be applied to elements which accept child nodes.
8859          * @param {String} msg (optional) A message to display in the mask
8860          * @param {String} msgCls (optional) A css class to apply to the msg element
8861          * @return {Element} The mask  element
8862          */
8863         mask : function(msg, msgCls){
8864             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  * Global Ajax request class.
11558  * @class Roo.Ajax
11559  * @extends Roo.data.Connection
11560  * @instanceOf  Roo.data.Connection
11561  */
11562 Roo.Ajax = new Roo.data.Connection({
11563     // fix up the docs
11564     
11565     /**
11566      * fix up scoping
11567      * @scope Roo.Ajax
11568      */
11569     
11570    /**
11571      * @cfg {String} url @hide
11572      */
11573     /**
11574      * @cfg {Object} extraParams @hide
11575      */
11576     /**
11577      * @cfg {Object} defaultHeaders @hide
11578      */
11579     /**
11580      * @cfg {String} method (Optional) @hide
11581      */
11582     /**
11583      * @cfg {Number} timeout (Optional) @hide
11584      */
11585     /**
11586      * @cfg {Boolean} autoAbort (Optional) @hide
11587      */
11588
11589     /**
11590      * @cfg {Boolean} disableCaching (Optional) @hide
11591      */
11592
11593     /**
11594      * @property  disableCaching
11595      * True to add a unique cache-buster param to GET requests. (defaults to true)
11596      * @type Boolean
11597      */
11598     /**
11599      * @property  url
11600      * The default URL to be used for requests to the server. (defaults to undefined)
11601      * @type String
11602      */
11603     /**
11604      * @property  extraParams
11605      * An object containing properties which are used as
11606      * extra parameters to each request made by this object. (defaults to undefined)
11607      * @type Object
11608      */
11609     /**
11610      * @property  defaultHeaders
11611      * An object containing request headers which are added to each request made by this object. (defaults to undefined)
11612      * @type Object
11613      */
11614     /**
11615      * @property  method
11616      * The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11617      * @type String
11618      */
11619     /**
11620      * @property  timeout
11621      * The timeout in milliseconds to be used for requests. (defaults to 30000)
11622      * @type Number
11623      */
11624
11625     /**
11626      * @property  autoAbort
11627      * Whether a new request should abort any pending requests. (defaults to false)
11628      * @type Boolean
11629      */
11630     autoAbort : false,
11631
11632     /**
11633      * Serialize the passed form into a url encoded string
11634      * @param {String/HTMLElement} form
11635      * @return {String}
11636      */
11637     serializeForm : function(form){
11638         return Roo.lib.Ajax.serializeForm(form);
11639     }
11640 });/*
11641  * Based on:
11642  * Ext JS Library 1.1.1
11643  * Copyright(c) 2006-2007, Ext JS, LLC.
11644  *
11645  * Originally Released Under LGPL - original licence link has changed is not relivant.
11646  *
11647  * Fork - LGPL
11648  * <script type="text/javascript">
11649  */
11650
11651  
11652 /**
11653  * @class Roo.UpdateManager
11654  * @extends Roo.util.Observable
11655  * Provides AJAX-style update for Element object.<br><br>
11656  * Usage:<br>
11657  * <pre><code>
11658  * // Get it from a Roo.Element object
11659  * var el = Roo.get("foo");
11660  * var mgr = el.getUpdateManager();
11661  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11662  * ...
11663  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11664  * <br>
11665  * // or directly (returns the same UpdateManager instance)
11666  * var mgr = new Roo.UpdateManager("myElementId");
11667  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11668  * mgr.on("update", myFcnNeedsToKnow);
11669  * <br>
11670    // short handed call directly from the element object
11671    Roo.get("foo").load({
11672         url: "bar.php",
11673         scripts:true,
11674         params: "for=bar",
11675         text: "Loading Foo..."
11676    });
11677  * </code></pre>
11678  * @constructor
11679  * Create new UpdateManager directly.
11680  * @param {String/HTMLElement/Roo.Element} el The element to update
11681  * @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).
11682  */
11683 Roo.UpdateManager = function(el, forceNew){
11684     el = Roo.get(el);
11685     if(!forceNew && el.updateManager){
11686         return el.updateManager;
11687     }
11688     /**
11689      * The Element object
11690      * @type Roo.Element
11691      */
11692     this.el = el;
11693     /**
11694      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11695      * @type String
11696      */
11697     this.defaultUrl = null;
11698
11699     this.addEvents({
11700         /**
11701          * @event beforeupdate
11702          * Fired before an update is made, return false from your handler and the update is cancelled.
11703          * @param {Roo.Element} el
11704          * @param {String/Object/Function} url
11705          * @param {String/Object} params
11706          */
11707         "beforeupdate": true,
11708         /**
11709          * @event update
11710          * Fired after successful update is made.
11711          * @param {Roo.Element} el
11712          * @param {Object} oResponseObject The response Object
11713          */
11714         "update": true,
11715         /**
11716          * @event failure
11717          * Fired on update failure.
11718          * @param {Roo.Element} el
11719          * @param {Object} oResponseObject The response Object
11720          */
11721         "failure": true
11722     });
11723     var d = Roo.UpdateManager.defaults;
11724     /**
11725      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11726      * @type String
11727      */
11728     this.sslBlankUrl = d.sslBlankUrl;
11729     /**
11730      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11731      * @type Boolean
11732      */
11733     this.disableCaching = d.disableCaching;
11734     /**
11735      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11736      * @type String
11737      */
11738     this.indicatorText = d.indicatorText;
11739     /**
11740      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11741      * @type String
11742      */
11743     this.showLoadIndicator = d.showLoadIndicator;
11744     /**
11745      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11746      * @type Number
11747      */
11748     this.timeout = d.timeout;
11749
11750     /**
11751      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11752      * @type Boolean
11753      */
11754     this.loadScripts = d.loadScripts;
11755
11756     /**
11757      * Transaction object of current executing transaction
11758      */
11759     this.transaction = null;
11760
11761     /**
11762      * @private
11763      */
11764     this.autoRefreshProcId = null;
11765     /**
11766      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11767      * @type Function
11768      */
11769     this.refreshDelegate = this.refresh.createDelegate(this);
11770     /**
11771      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11772      * @type Function
11773      */
11774     this.updateDelegate = this.update.createDelegate(this);
11775     /**
11776      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11777      * @type Function
11778      */
11779     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11780     /**
11781      * @private
11782      */
11783     this.successDelegate = this.processSuccess.createDelegate(this);
11784     /**
11785      * @private
11786      */
11787     this.failureDelegate = this.processFailure.createDelegate(this);
11788
11789     if(!this.renderer){
11790      /**
11791       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11792       */
11793     this.renderer = new Roo.UpdateManager.BasicRenderer();
11794     }
11795     
11796     Roo.UpdateManager.superclass.constructor.call(this);
11797 };
11798
11799 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11800     /**
11801      * Get the Element this UpdateManager is bound to
11802      * @return {Roo.Element} The element
11803      */
11804     getEl : function(){
11805         return this.el;
11806     },
11807     /**
11808      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11809      * @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:
11810 <pre><code>
11811 um.update({<br/>
11812     url: "your-url.php",<br/>
11813     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11814     callback: yourFunction,<br/>
11815     scope: yourObject, //(optional scope)  <br/>
11816     discardUrl: false, <br/>
11817     nocache: false,<br/>
11818     text: "Loading...",<br/>
11819     timeout: 30,<br/>
11820     scripts: false<br/>
11821 });
11822 </code></pre>
11823      * The only required property is url. The optional properties nocache, text and scripts
11824      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11825      * @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}
11826      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11827      * @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.
11828      */
11829     update : function(url, params, callback, discardUrl){
11830         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11831             var method = this.method, cfg;
11832             if(typeof url == "object"){ // must be config object
11833                 cfg = url;
11834                 url = cfg.url;
11835                 params = params || cfg.params;
11836                 callback = callback || cfg.callback;
11837                 discardUrl = discardUrl || cfg.discardUrl;
11838                 if(callback && cfg.scope){
11839                     callback = callback.createDelegate(cfg.scope);
11840                 }
11841                 if(typeof cfg.method != "undefined"){method = cfg.method;};
11842                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
11843                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
11844                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
11845                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
11846             }
11847             this.showLoading();
11848             if(!discardUrl){
11849                 this.defaultUrl = url;
11850             }
11851             if(typeof url == "function"){
11852                 url = url.call(this);
11853             }
11854
11855             method = method || (params ? "POST" : "GET");
11856             if(method == "GET"){
11857                 url = this.prepareUrl(url);
11858             }
11859
11860             var o = Roo.apply(cfg ||{}, {
11861                 url : url,
11862                 params: params,
11863                 success: this.successDelegate,
11864                 failure: this.failureDelegate,
11865                 callback: undefined,
11866                 timeout: (this.timeout*1000),
11867                 argument: {"url": url, "form": null, "callback": callback, "params": params}
11868             });
11869
11870             this.transaction = Roo.Ajax.request(o);
11871         }
11872     },
11873
11874     /**
11875      * 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.
11876      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
11877      * @param {String/HTMLElement} form The form Id or form element
11878      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
11879      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
11880      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11881      */
11882     formUpdate : function(form, url, reset, callback){
11883         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
11884             if(typeof url == "function"){
11885                 url = url.call(this);
11886             }
11887             form = Roo.getDom(form);
11888             this.transaction = Roo.Ajax.request({
11889                 form: form,
11890                 url:url,
11891                 success: this.successDelegate,
11892                 failure: this.failureDelegate,
11893                 timeout: (this.timeout*1000),
11894                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
11895             });
11896             this.showLoading.defer(1, this);
11897         }
11898     },
11899
11900     /**
11901      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
11902      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11903      */
11904     refresh : function(callback){
11905         if(this.defaultUrl == null){
11906             return;
11907         }
11908         this.update(this.defaultUrl, null, callback, true);
11909     },
11910
11911     /**
11912      * Set this element to auto refresh.
11913      * @param {Number} interval How often to update (in seconds).
11914      * @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)
11915      * @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}
11916      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11917      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
11918      */
11919     startAutoRefresh : function(interval, url, params, callback, refreshNow){
11920         if(refreshNow){
11921             this.update(url || this.defaultUrl, params, callback, true);
11922         }
11923         if(this.autoRefreshProcId){
11924             clearInterval(this.autoRefreshProcId);
11925         }
11926         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
11927     },
11928
11929     /**
11930      * Stop auto refresh on this element.
11931      */
11932      stopAutoRefresh : function(){
11933         if(this.autoRefreshProcId){
11934             clearInterval(this.autoRefreshProcId);
11935             delete this.autoRefreshProcId;
11936         }
11937     },
11938
11939     isAutoRefreshing : function(){
11940        return this.autoRefreshProcId ? true : false;
11941     },
11942     /**
11943      * Called to update the element to "Loading" state. Override to perform custom action.
11944      */
11945     showLoading : function(){
11946         if(this.showLoadIndicator){
11947             this.el.update(this.indicatorText);
11948         }
11949     },
11950
11951     /**
11952      * Adds unique parameter to query string if disableCaching = true
11953      * @private
11954      */
11955     prepareUrl : function(url){
11956         if(this.disableCaching){
11957             var append = "_dc=" + (new Date().getTime());
11958             if(url.indexOf("?") !== -1){
11959                 url += "&" + append;
11960             }else{
11961                 url += "?" + append;
11962             }
11963         }
11964         return url;
11965     },
11966
11967     /**
11968      * @private
11969      */
11970     processSuccess : function(response){
11971         this.transaction = null;
11972         if(response.argument.form && response.argument.reset){
11973             try{ // put in try/catch since some older FF releases had problems with this
11974                 response.argument.form.reset();
11975             }catch(e){}
11976         }
11977         if(this.loadScripts){
11978             this.renderer.render(this.el, response, this,
11979                 this.updateComplete.createDelegate(this, [response]));
11980         }else{
11981             this.renderer.render(this.el, response, this);
11982             this.updateComplete(response);
11983         }
11984     },
11985
11986     updateComplete : function(response){
11987         this.fireEvent("update", this.el, response);
11988         if(typeof response.argument.callback == "function"){
11989             response.argument.callback(this.el, true, response);
11990         }
11991     },
11992
11993     /**
11994      * @private
11995      */
11996     processFailure : function(response){
11997         this.transaction = null;
11998         this.fireEvent("failure", this.el, response);
11999         if(typeof response.argument.callback == "function"){
12000             response.argument.callback(this.el, false, response);
12001         }
12002     },
12003
12004     /**
12005      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
12006      * @param {Object} renderer The object implementing the render() method
12007      */
12008     setRenderer : function(renderer){
12009         this.renderer = renderer;
12010     },
12011
12012     getRenderer : function(){
12013        return this.renderer;
12014     },
12015
12016     /**
12017      * Set the defaultUrl used for updates
12018      * @param {String/Function} defaultUrl The url or a function to call to get the url
12019      */
12020     setDefaultUrl : function(defaultUrl){
12021         this.defaultUrl = defaultUrl;
12022     },
12023
12024     /**
12025      * Aborts the executing transaction
12026      */
12027     abort : function(){
12028         if(this.transaction){
12029             Roo.Ajax.abort(this.transaction);
12030         }
12031     },
12032
12033     /**
12034      * Returns true if an update is in progress
12035      * @return {Boolean}
12036      */
12037     isUpdating : function(){
12038         if(this.transaction){
12039             return Roo.Ajax.isLoading(this.transaction);
12040         }
12041         return false;
12042     }
12043 });
12044
12045 /**
12046  * @class Roo.UpdateManager.defaults
12047  * @static (not really - but it helps the doc tool)
12048  * The defaults collection enables customizing the default properties of UpdateManager
12049  */
12050    Roo.UpdateManager.defaults = {
12051        /**
12052          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12053          * @type Number
12054          */
12055          timeout : 30,
12056
12057          /**
12058          * True to process scripts by default (Defaults to false).
12059          * @type Boolean
12060          */
12061         loadScripts : false,
12062
12063         /**
12064         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12065         * @type String
12066         */
12067         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12068         /**
12069          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12070          * @type Boolean
12071          */
12072         disableCaching : false,
12073         /**
12074          * Whether to show indicatorText when loading (Defaults to true).
12075          * @type Boolean
12076          */
12077         showLoadIndicator : true,
12078         /**
12079          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12080          * @type String
12081          */
12082         indicatorText : '<div class="loading-indicator">Loading...</div>'
12083    };
12084
12085 /**
12086  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12087  *Usage:
12088  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12089  * @param {String/HTMLElement/Roo.Element} el The element to update
12090  * @param {String} url The url
12091  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12092  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12093  * @static
12094  * @deprecated
12095  * @member Roo.UpdateManager
12096  */
12097 Roo.UpdateManager.updateElement = function(el, url, params, options){
12098     var um = Roo.get(el, true).getUpdateManager();
12099     Roo.apply(um, options);
12100     um.update(url, params, options ? options.callback : null);
12101 };
12102 // alias for backwards compat
12103 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12104 /**
12105  * @class Roo.UpdateManager.BasicRenderer
12106  * Default Content renderer. Updates the elements innerHTML with the responseText.
12107  */
12108 Roo.UpdateManager.BasicRenderer = function(){};
12109
12110 Roo.UpdateManager.BasicRenderer.prototype = {
12111     /**
12112      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12113      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12114      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12115      * @param {Roo.Element} el The element being rendered
12116      * @param {Object} response The YUI Connect response object
12117      * @param {UpdateManager} updateManager The calling update manager
12118      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12119      */
12120      render : function(el, response, updateManager, callback){
12121         el.update(response.responseText, updateManager.loadScripts, callback);
12122     }
12123 };
12124 /*
12125  * Based on:
12126  * Ext JS Library 1.1.1
12127  * Copyright(c) 2006-2007, Ext JS, LLC.
12128  *
12129  * Originally Released Under LGPL - original licence link has changed is not relivant.
12130  *
12131  * Fork - LGPL
12132  * <script type="text/javascript">
12133  */
12134
12135 /**
12136  * @class Roo.util.DelayedTask
12137  * Provides a convenient method of performing setTimeout where a new
12138  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12139  * You can use this class to buffer
12140  * the keypress events for a certain number of milliseconds, and perform only if they stop
12141  * for that amount of time.
12142  * @constructor The parameters to this constructor serve as defaults and are not required.
12143  * @param {Function} fn (optional) The default function to timeout
12144  * @param {Object} scope (optional) The default scope of that timeout
12145  * @param {Array} args (optional) The default Array of arguments
12146  */
12147 Roo.util.DelayedTask = function(fn, scope, args){
12148     var id = null, d, t;
12149
12150     var call = function(){
12151         var now = new Date().getTime();
12152         if(now - t >= d){
12153             clearInterval(id);
12154             id = null;
12155             fn.apply(scope, args || []);
12156         }
12157     };
12158     /**
12159      * Cancels any pending timeout and queues a new one
12160      * @param {Number} delay The milliseconds to delay
12161      * @param {Function} newFn (optional) Overrides function passed to constructor
12162      * @param {Object} newScope (optional) Overrides scope passed to constructor
12163      * @param {Array} newArgs (optional) Overrides args passed to constructor
12164      */
12165     this.delay = function(delay, newFn, newScope, newArgs){
12166         if(id && delay != d){
12167             this.cancel();
12168         }
12169         d = delay;
12170         t = new Date().getTime();
12171         fn = newFn || fn;
12172         scope = newScope || scope;
12173         args = newArgs || args;
12174         if(!id){
12175             id = setInterval(call, d);
12176         }
12177     };
12178
12179     /**
12180      * Cancel the last queued timeout
12181      */
12182     this.cancel = function(){
12183         if(id){
12184             clearInterval(id);
12185             id = null;
12186         }
12187     };
12188 };/*
12189  * Based on:
12190  * Ext JS Library 1.1.1
12191  * Copyright(c) 2006-2007, Ext JS, LLC.
12192  *
12193  * Originally Released Under LGPL - original licence link has changed is not relivant.
12194  *
12195  * Fork - LGPL
12196  * <script type="text/javascript">
12197  */
12198  
12199  
12200 Roo.util.TaskRunner = function(interval){
12201     interval = interval || 10;
12202     var tasks = [], removeQueue = [];
12203     var id = 0;
12204     var running = false;
12205
12206     var stopThread = function(){
12207         running = false;
12208         clearInterval(id);
12209         id = 0;
12210     };
12211
12212     var startThread = function(){
12213         if(!running){
12214             running = true;
12215             id = setInterval(runTasks, interval);
12216         }
12217     };
12218
12219     var removeTask = function(task){
12220         removeQueue.push(task);
12221         if(task.onStop){
12222             task.onStop();
12223         }
12224     };
12225
12226     var runTasks = function(){
12227         if(removeQueue.length > 0){
12228             for(var i = 0, len = removeQueue.length; i < len; i++){
12229                 tasks.remove(removeQueue[i]);
12230             }
12231             removeQueue = [];
12232             if(tasks.length < 1){
12233                 stopThread();
12234                 return;
12235             }
12236         }
12237         var now = new Date().getTime();
12238         for(var i = 0, len = tasks.length; i < len; ++i){
12239             var t = tasks[i];
12240             var itime = now - t.taskRunTime;
12241             if(t.interval <= itime){
12242                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12243                 t.taskRunTime = now;
12244                 if(rt === false || t.taskRunCount === t.repeat){
12245                     removeTask(t);
12246                     return;
12247                 }
12248             }
12249             if(t.duration && t.duration <= (now - t.taskStartTime)){
12250                 removeTask(t);
12251             }
12252         }
12253     };
12254
12255     /**
12256      * Queues a new task.
12257      * @param {Object} task
12258      */
12259     this.start = function(task){
12260         tasks.push(task);
12261         task.taskStartTime = new Date().getTime();
12262         task.taskRunTime = 0;
12263         task.taskRunCount = 0;
12264         startThread();
12265         return task;
12266     };
12267
12268     this.stop = function(task){
12269         removeTask(task);
12270         return task;
12271     };
12272
12273     this.stopAll = function(){
12274         stopThread();
12275         for(var i = 0, len = tasks.length; i < len; i++){
12276             if(tasks[i].onStop){
12277                 tasks[i].onStop();
12278             }
12279         }
12280         tasks = [];
12281         removeQueue = [];
12282     };
12283 };
12284
12285 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12286  * Based on:
12287  * Ext JS Library 1.1.1
12288  * Copyright(c) 2006-2007, Ext JS, LLC.
12289  *
12290  * Originally Released Under LGPL - original licence link has changed is not relivant.
12291  *
12292  * Fork - LGPL
12293  * <script type="text/javascript">
12294  */
12295
12296  
12297 /**
12298  * @class Roo.util.MixedCollection
12299  * @extends Roo.util.Observable
12300  * A Collection class that maintains both numeric indexes and keys and exposes events.
12301  * @constructor
12302  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12303  * collection (defaults to false)
12304  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12305  * and return the key value for that item.  This is used when available to look up the key on items that
12306  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12307  * equivalent to providing an implementation for the {@link #getKey} method.
12308  */
12309 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12310     this.items = [];
12311     this.map = {};
12312     this.keys = [];
12313     this.length = 0;
12314     this.addEvents({
12315         /**
12316          * @event clear
12317          * Fires when the collection is cleared.
12318          */
12319         "clear" : true,
12320         /**
12321          * @event add
12322          * Fires when an item is added to the collection.
12323          * @param {Number} index The index at which the item was added.
12324          * @param {Object} o The item added.
12325          * @param {String} key The key associated with the added item.
12326          */
12327         "add" : true,
12328         /**
12329          * @event replace
12330          * Fires when an item is replaced in the collection.
12331          * @param {String} key he key associated with the new added.
12332          * @param {Object} old The item being replaced.
12333          * @param {Object} new The new item.
12334          */
12335         "replace" : true,
12336         /**
12337          * @event remove
12338          * Fires when an item is removed from the collection.
12339          * @param {Object} o The item being removed.
12340          * @param {String} key (optional) The key associated with the removed item.
12341          */
12342         "remove" : true,
12343         "sort" : true
12344     });
12345     this.allowFunctions = allowFunctions === true;
12346     if(keyFn){
12347         this.getKey = keyFn;
12348     }
12349     Roo.util.MixedCollection.superclass.constructor.call(this);
12350 };
12351
12352 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12353     allowFunctions : false,
12354     
12355 /**
12356  * Adds an item to the collection.
12357  * @param {String} key The key to associate with the item
12358  * @param {Object} o The item to add.
12359  * @return {Object} The item added.
12360  */
12361     add : function(key, o){
12362         if(arguments.length == 1){
12363             o = arguments[0];
12364             key = this.getKey(o);
12365         }
12366         if(typeof key == "undefined" || key === null){
12367             this.length++;
12368             this.items.push(o);
12369             this.keys.push(null);
12370         }else{
12371             var old = this.map[key];
12372             if(old){
12373                 return this.replace(key, o);
12374             }
12375             this.length++;
12376             this.items.push(o);
12377             this.map[key] = o;
12378             this.keys.push(key);
12379         }
12380         this.fireEvent("add", this.length-1, o, key);
12381         return o;
12382     },
12383        
12384 /**
12385   * MixedCollection has a generic way to fetch keys if you implement getKey.
12386 <pre><code>
12387 // normal way
12388 var mc = new Roo.util.MixedCollection();
12389 mc.add(someEl.dom.id, someEl);
12390 mc.add(otherEl.dom.id, otherEl);
12391 //and so on
12392
12393 // using getKey
12394 var mc = new Roo.util.MixedCollection();
12395 mc.getKey = function(el){
12396    return el.dom.id;
12397 };
12398 mc.add(someEl);
12399 mc.add(otherEl);
12400
12401 // or via the constructor
12402 var mc = new Roo.util.MixedCollection(false, function(el){
12403    return el.dom.id;
12404 });
12405 mc.add(someEl);
12406 mc.add(otherEl);
12407 </code></pre>
12408  * @param o {Object} The item for which to find the key.
12409  * @return {Object} The key for the passed item.
12410  */
12411     getKey : function(o){
12412          return o.id; 
12413     },
12414    
12415 /**
12416  * Replaces an item in the collection.
12417  * @param {String} key The key associated with the item to replace, or the item to replace.
12418  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
12419  * @return {Object}  The new item.
12420  */
12421     replace : function(key, o){
12422         if(arguments.length == 1){
12423             o = arguments[0];
12424             key = this.getKey(o);
12425         }
12426         var old = this.item(key);
12427         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
12428              return this.add(key, o);
12429         }
12430         var index = this.indexOfKey(key);
12431         this.items[index] = o;
12432         this.map[key] = o;
12433         this.fireEvent("replace", key, old, o);
12434         return o;
12435     },
12436    
12437 /**
12438  * Adds all elements of an Array or an Object to the collection.
12439  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
12440  * an Array of values, each of which are added to the collection.
12441  */
12442     addAll : function(objs){
12443         if(arguments.length > 1 || objs instanceof Array){
12444             var args = arguments.length > 1 ? arguments : objs;
12445             for(var i = 0, len = args.length; i < len; i++){
12446                 this.add(args[i]);
12447             }
12448         }else{
12449             for(var key in objs){
12450                 if(this.allowFunctions || typeof objs[key] != "function"){
12451                     this.add(key, objs[key]);
12452                 }
12453             }
12454         }
12455     },
12456    
12457 /**
12458  * Executes the specified function once for every item in the collection, passing each
12459  * item as the first and only parameter. returning false from the function will stop the iteration.
12460  * @param {Function} fn The function to execute for each item.
12461  * @param {Object} scope (optional) The scope in which to execute the function.
12462  */
12463     each : function(fn, scope){
12464         var items = [].concat(this.items); // each safe for removal
12465         for(var i = 0, len = items.length; i < len; i++){
12466             if(fn.call(scope || items[i], items[i], i, len) === false){
12467                 break;
12468             }
12469         }
12470     },
12471    
12472 /**
12473  * Executes the specified function once for every key in the collection, passing each
12474  * key, and its associated item as the first two parameters.
12475  * @param {Function} fn The function to execute for each item.
12476  * @param {Object} scope (optional) The scope in which to execute the function.
12477  */
12478     eachKey : function(fn, scope){
12479         for(var i = 0, len = this.keys.length; i < len; i++){
12480             fn.call(scope || window, this.keys[i], this.items[i], i, len);
12481         }
12482     },
12483    
12484 /**
12485  * Returns the first item in the collection which elicits a true return value from the
12486  * passed selection function.
12487  * @param {Function} fn The selection function to execute for each item.
12488  * @param {Object} scope (optional) The scope in which to execute the function.
12489  * @return {Object} The first item in the collection which returned true from the selection function.
12490  */
12491     find : function(fn, scope){
12492         for(var i = 0, len = this.items.length; i < len; i++){
12493             if(fn.call(scope || window, this.items[i], this.keys[i])){
12494                 return this.items[i];
12495             }
12496         }
12497         return null;
12498     },
12499    
12500 /**
12501  * Inserts an item at the specified index in the collection.
12502  * @param {Number} index The index to insert the item at.
12503  * @param {String} key The key to associate with the new item, or the item itself.
12504  * @param {Object} o  (optional) If the second parameter was a key, the new item.
12505  * @return {Object} The item inserted.
12506  */
12507     insert : function(index, key, o){
12508         if(arguments.length == 2){
12509             o = arguments[1];
12510             key = this.getKey(o);
12511         }
12512         if(index >= this.length){
12513             return this.add(key, o);
12514         }
12515         this.length++;
12516         this.items.splice(index, 0, o);
12517         if(typeof key != "undefined" && key != null){
12518             this.map[key] = o;
12519         }
12520         this.keys.splice(index, 0, key);
12521         this.fireEvent("add", index, o, key);
12522         return o;
12523     },
12524    
12525 /**
12526  * Removed an item from the collection.
12527  * @param {Object} o The item to remove.
12528  * @return {Object} The item removed.
12529  */
12530     remove : function(o){
12531         return this.removeAt(this.indexOf(o));
12532     },
12533    
12534 /**
12535  * Remove an item from a specified index in the collection.
12536  * @param {Number} index The index within the collection of the item to remove.
12537  */
12538     removeAt : function(index){
12539         if(index < this.length && index >= 0){
12540             this.length--;
12541             var o = this.items[index];
12542             this.items.splice(index, 1);
12543             var key = this.keys[index];
12544             if(typeof key != "undefined"){
12545                 delete this.map[key];
12546             }
12547             this.keys.splice(index, 1);
12548             this.fireEvent("remove", o, key);
12549         }
12550     },
12551    
12552 /**
12553  * Removed an item associated with the passed key fom the collection.
12554  * @param {String} key The key of the item to remove.
12555  */
12556     removeKey : function(key){
12557         return this.removeAt(this.indexOfKey(key));
12558     },
12559    
12560 /**
12561  * Returns the number of items in the collection.
12562  * @return {Number} the number of items in the collection.
12563  */
12564     getCount : function(){
12565         return this.length; 
12566     },
12567    
12568 /**
12569  * Returns index within the collection of the passed Object.
12570  * @param {Object} o The item to find the index of.
12571  * @return {Number} index of the item.
12572  */
12573     indexOf : function(o){
12574         if(!this.items.indexOf){
12575             for(var i = 0, len = this.items.length; i < len; i++){
12576                 if(this.items[i] == o) return i;
12577             }
12578             return -1;
12579         }else{
12580             return this.items.indexOf(o);
12581         }
12582     },
12583    
12584 /**
12585  * Returns index within the collection of the passed key.
12586  * @param {String} key The key to find the index of.
12587  * @return {Number} index of the key.
12588  */
12589     indexOfKey : function(key){
12590         if(!this.keys.indexOf){
12591             for(var i = 0, len = this.keys.length; i < len; i++){
12592                 if(this.keys[i] == key) return i;
12593             }
12594             return -1;
12595         }else{
12596             return this.keys.indexOf(key);
12597         }
12598     },
12599    
12600 /**
12601  * Returns the item associated with the passed key OR index. Key has priority over index.
12602  * @param {String/Number} key The key or index of the item.
12603  * @return {Object} The item associated with the passed key.
12604  */
12605     item : function(key){
12606         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
12607         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
12608     },
12609     
12610 /**
12611  * Returns the item at the specified index.
12612  * @param {Number} index The index of the item.
12613  * @return {Object}
12614  */
12615     itemAt : function(index){
12616         return this.items[index];
12617     },
12618     
12619 /**
12620  * Returns the item associated with the passed key.
12621  * @param {String/Number} key The key of the item.
12622  * @return {Object} The item associated with the passed key.
12623  */
12624     key : function(key){
12625         return this.map[key];
12626     },
12627    
12628 /**
12629  * Returns true if the collection contains the passed Object as an item.
12630  * @param {Object} o  The Object to look for in the collection.
12631  * @return {Boolean} True if the collection contains the Object as an item.
12632  */
12633     contains : function(o){
12634         return this.indexOf(o) != -1;
12635     },
12636    
12637 /**
12638  * Returns true if the collection contains the passed Object as a key.
12639  * @param {String} key The key to look for in the collection.
12640  * @return {Boolean} True if the collection contains the Object as a key.
12641  */
12642     containsKey : function(key){
12643         return typeof this.map[key] != "undefined";
12644     },
12645    
12646 /**
12647  * Removes all items from the collection.
12648  */
12649     clear : function(){
12650         this.length = 0;
12651         this.items = [];
12652         this.keys = [];
12653         this.map = {};
12654         this.fireEvent("clear");
12655     },
12656    
12657 /**
12658  * Returns the first item in the collection.
12659  * @return {Object} the first item in the collection..
12660  */
12661     first : function(){
12662         return this.items[0]; 
12663     },
12664    
12665 /**
12666  * Returns the last item in the collection.
12667  * @return {Object} the last item in the collection..
12668  */
12669     last : function(){
12670         return this.items[this.length-1];   
12671     },
12672     
12673     _sort : function(property, dir, fn){
12674         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
12675         fn = fn || function(a, b){
12676             return a-b;
12677         };
12678         var c = [], k = this.keys, items = this.items;
12679         for(var i = 0, len = items.length; i < len; i++){
12680             c[c.length] = {key: k[i], value: items[i], index: i};
12681         }
12682         c.sort(function(a, b){
12683             var v = fn(a[property], b[property]) * dsc;
12684             if(v == 0){
12685                 v = (a.index < b.index ? -1 : 1);
12686             }
12687             return v;
12688         });
12689         for(var i = 0, len = c.length; i < len; i++){
12690             items[i] = c[i].value;
12691             k[i] = c[i].key;
12692         }
12693         this.fireEvent("sort", this);
12694     },
12695     
12696     /**
12697      * Sorts this collection with the passed comparison function
12698      * @param {String} direction (optional) "ASC" or "DESC"
12699      * @param {Function} fn (optional) comparison function
12700      */
12701     sort : function(dir, fn){
12702         this._sort("value", dir, fn);
12703     },
12704     
12705     /**
12706      * Sorts this collection by keys
12707      * @param {String} direction (optional) "ASC" or "DESC"
12708      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
12709      */
12710     keySort : function(dir, fn){
12711         this._sort("key", dir, fn || function(a, b){
12712             return String(a).toUpperCase()-String(b).toUpperCase();
12713         });
12714     },
12715     
12716     /**
12717      * Returns a range of items in this collection
12718      * @param {Number} startIndex (optional) defaults to 0
12719      * @param {Number} endIndex (optional) default to the last item
12720      * @return {Array} An array of items
12721      */
12722     getRange : function(start, end){
12723         var items = this.items;
12724         if(items.length < 1){
12725             return [];
12726         }
12727         start = start || 0;
12728         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
12729         var r = [];
12730         if(start <= end){
12731             for(var i = start; i <= end; i++) {
12732                     r[r.length] = items[i];
12733             }
12734         }else{
12735             for(var i = start; i >= end; i--) {
12736                     r[r.length] = items[i];
12737             }
12738         }
12739         return r;
12740     },
12741         
12742     /**
12743      * Filter the <i>objects</i> in this collection by a specific property. 
12744      * Returns a new collection that has been filtered.
12745      * @param {String} property A property on your objects
12746      * @param {String/RegExp} value Either string that the property values 
12747      * should start with or a RegExp to test against the property
12748      * @return {MixedCollection} The new filtered collection
12749      */
12750     filter : function(property, value){
12751         if(!value.exec){ // not a regex
12752             value = String(value);
12753             if(value.length == 0){
12754                 return this.clone();
12755             }
12756             value = new RegExp("^" + Roo.escapeRe(value), "i");
12757         }
12758         return this.filterBy(function(o){
12759             return o && value.test(o[property]);
12760         });
12761         },
12762     
12763     /**
12764      * Filter by a function. * Returns a new collection that has been filtered.
12765      * The passed function will be called with each 
12766      * object in the collection. If the function returns true, the value is included 
12767      * otherwise it is filtered.
12768      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
12769      * @param {Object} scope (optional) The scope of the function (defaults to this) 
12770      * @return {MixedCollection} The new filtered collection
12771      */
12772     filterBy : function(fn, scope){
12773         var r = new Roo.util.MixedCollection();
12774         r.getKey = this.getKey;
12775         var k = this.keys, it = this.items;
12776         for(var i = 0, len = it.length; i < len; i++){
12777             if(fn.call(scope||this, it[i], k[i])){
12778                                 r.add(k[i], it[i]);
12779                         }
12780         }
12781         return r;
12782     },
12783     
12784     /**
12785      * Creates a duplicate of this collection
12786      * @return {MixedCollection}
12787      */
12788     clone : function(){
12789         var r = new Roo.util.MixedCollection();
12790         var k = this.keys, it = this.items;
12791         for(var i = 0, len = it.length; i < len; i++){
12792             r.add(k[i], it[i]);
12793         }
12794         r.getKey = this.getKey;
12795         return r;
12796     }
12797 });
12798 /**
12799  * Returns the item associated with the passed key or index.
12800  * @method
12801  * @param {String/Number} key The key or index of the item.
12802  * @return {Object} The item associated with the passed key.
12803  */
12804 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
12805  * Based on:
12806  * Ext JS Library 1.1.1
12807  * Copyright(c) 2006-2007, Ext JS, LLC.
12808  *
12809  * Originally Released Under LGPL - original licence link has changed is not relivant.
12810  *
12811  * Fork - LGPL
12812  * <script type="text/javascript">
12813  */
12814 /**
12815  * @class Roo.util.JSON
12816  * Modified version of Douglas Crockford"s json.js that doesn"t
12817  * mess with the Object prototype 
12818  * http://www.json.org/js.html
12819  * @singleton
12820  */
12821 Roo.util.JSON = new (function(){
12822     var useHasOwn = {}.hasOwnProperty ? true : false;
12823     
12824     // crashes Safari in some instances
12825     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
12826     
12827     var pad = function(n) {
12828         return n < 10 ? "0" + n : n;
12829     };
12830     
12831     var m = {
12832         "\b": '\\b',
12833         "\t": '\\t',
12834         "\n": '\\n',
12835         "\f": '\\f',
12836         "\r": '\\r',
12837         '"' : '\\"',
12838         "\\": '\\\\'
12839     };
12840
12841     var encodeString = function(s){
12842         if (/["\\\x00-\x1f]/.test(s)) {
12843             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
12844                 var c = m[b];
12845                 if(c){
12846                     return c;
12847                 }
12848                 c = b.charCodeAt();
12849                 return "\\u00" +
12850                     Math.floor(c / 16).toString(16) +
12851                     (c % 16).toString(16);
12852             }) + '"';
12853         }
12854         return '"' + s + '"';
12855     };
12856     
12857     var encodeArray = function(o){
12858         var a = ["["], b, i, l = o.length, v;
12859             for (i = 0; i < l; i += 1) {
12860                 v = o[i];
12861                 switch (typeof v) {
12862                     case "undefined":
12863                     case "function":
12864                     case "unknown":
12865                         break;
12866                     default:
12867                         if (b) {
12868                             a.push(',');
12869                         }
12870                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
12871                         b = true;
12872                 }
12873             }
12874             a.push("]");
12875             return a.join("");
12876     };
12877     
12878     var encodeDate = function(o){
12879         return '"' + o.getFullYear() + "-" +
12880                 pad(o.getMonth() + 1) + "-" +
12881                 pad(o.getDate()) + "T" +
12882                 pad(o.getHours()) + ":" +
12883                 pad(o.getMinutes()) + ":" +
12884                 pad(o.getSeconds()) + '"';
12885     };
12886     
12887     /**
12888      * Encodes an Object, Array or other value
12889      * @param {Mixed} o The variable to encode
12890      * @return {String} The JSON string
12891      */
12892     this.encode = function(o)
12893     {
12894         // should this be extended to fully wrap stringify..
12895         
12896         if(typeof o == "undefined" || o === null){
12897             return "null";
12898         }else if(o instanceof Array){
12899             return encodeArray(o);
12900         }else if(o instanceof Date){
12901             return encodeDate(o);
12902         }else if(typeof o == "string"){
12903             return encodeString(o);
12904         }else if(typeof o == "number"){
12905             return isFinite(o) ? String(o) : "null";
12906         }else if(typeof o == "boolean"){
12907             return String(o);
12908         }else {
12909             var a = ["{"], b, i, v;
12910             for (i in o) {
12911                 if(!useHasOwn || o.hasOwnProperty(i)) {
12912                     v = o[i];
12913                     switch (typeof v) {
12914                     case "undefined":
12915                     case "function":
12916                     case "unknown":
12917                         break;
12918                     default:
12919                         if(b){
12920                             a.push(',');
12921                         }
12922                         a.push(this.encode(i), ":",
12923                                 v === null ? "null" : this.encode(v));
12924                         b = true;
12925                     }
12926                 }
12927             }
12928             a.push("}");
12929             return a.join("");
12930         }
12931     };
12932     
12933     /**
12934      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
12935      * @param {String} json The JSON string
12936      * @return {Object} The resulting object
12937      */
12938     this.decode = function(json){
12939         
12940         return  /** eval:var:json */ eval("(" + json + ')');
12941     };
12942 })();
12943 /** 
12944  * Shorthand for {@link Roo.util.JSON#encode}
12945  * @member Roo encode 
12946  * @method */
12947 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
12948 /** 
12949  * Shorthand for {@link Roo.util.JSON#decode}
12950  * @member Roo decode 
12951  * @method */
12952 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
12953 /*
12954  * Based on:
12955  * Ext JS Library 1.1.1
12956  * Copyright(c) 2006-2007, Ext JS, LLC.
12957  *
12958  * Originally Released Under LGPL - original licence link has changed is not relivant.
12959  *
12960  * Fork - LGPL
12961  * <script type="text/javascript">
12962  */
12963  
12964 /**
12965  * @class Roo.util.Format
12966  * Reusable data formatting functions
12967  * @singleton
12968  */
12969 Roo.util.Format = function(){
12970     var trimRe = /^\s+|\s+$/g;
12971     return {
12972         /**
12973          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
12974          * @param {String} value The string to truncate
12975          * @param {Number} length The maximum length to allow before truncating
12976          * @return {String} The converted text
12977          */
12978         ellipsis : function(value, len){
12979             if(value && value.length > len){
12980                 return value.substr(0, len-3)+"...";
12981             }
12982             return value;
12983         },
12984
12985         /**
12986          * Checks a reference and converts it to empty string if it is undefined
12987          * @param {Mixed} value Reference to check
12988          * @return {Mixed} Empty string if converted, otherwise the original value
12989          */
12990         undef : function(value){
12991             return typeof value != "undefined" ? value : "";
12992         },
12993
12994         /**
12995          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
12996          * @param {String} value The string to encode
12997          * @return {String} The encoded text
12998          */
12999         htmlEncode : function(value){
13000             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
13001         },
13002
13003         /**
13004          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
13005          * @param {String} value The string to decode
13006          * @return {String} The decoded text
13007          */
13008         htmlDecode : function(value){
13009             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
13010         },
13011
13012         /**
13013          * Trims any whitespace from either side of a string
13014          * @param {String} value The text to trim
13015          * @return {String} The trimmed text
13016          */
13017         trim : function(value){
13018             return String(value).replace(trimRe, "");
13019         },
13020
13021         /**
13022          * Returns a substring from within an original string
13023          * @param {String} value The original text
13024          * @param {Number} start The start index of the substring
13025          * @param {Number} length The length of the substring
13026          * @return {String} The substring
13027          */
13028         substr : function(value, start, length){
13029             return String(value).substr(start, length);
13030         },
13031
13032         /**
13033          * Converts a string to all lower case letters
13034          * @param {String} value The text to convert
13035          * @return {String} The converted text
13036          */
13037         lowercase : function(value){
13038             return String(value).toLowerCase();
13039         },
13040
13041         /**
13042          * Converts a string to all upper case letters
13043          * @param {String} value The text to convert
13044          * @return {String} The converted text
13045          */
13046         uppercase : function(value){
13047             return String(value).toUpperCase();
13048         },
13049
13050         /**
13051          * Converts the first character only of a string to upper case
13052          * @param {String} value The text to convert
13053          * @return {String} The converted text
13054          */
13055         capitalize : function(value){
13056             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13057         },
13058
13059         // private
13060         call : function(value, fn){
13061             if(arguments.length > 2){
13062                 var args = Array.prototype.slice.call(arguments, 2);
13063                 args.unshift(value);
13064                  
13065                 return /** eval:var:value */  eval(fn).apply(window, args);
13066             }else{
13067                 /** eval:var:value */
13068                 return /** eval:var:value */ eval(fn).call(window, value);
13069             }
13070         },
13071
13072        
13073         /**
13074          * safer version of Math.toFixed..??/
13075          * @param {Number/String} value The numeric value to format
13076          * @param {Number/String} value Decimal places 
13077          * @return {String} The formatted currency string
13078          */
13079         toFixed : function(v, n)
13080         {
13081             // why not use to fixed - precision is buggered???
13082             if (!n) {
13083                 return Math.round(v-0);
13084             }
13085             var fact = Math.pow(10,n+1);
13086             v = (Math.round((v-0)*fact))/fact;
13087             var z = (''+fact).substring(2);
13088             if (v == Math.floor(v)) {
13089                 return Math.floor(v) + '.' + z;
13090             }
13091             
13092             // now just padd decimals..
13093             var ps = String(v).split('.');
13094             var fd = (ps[1] + z);
13095             var r = fd.substring(0,n); 
13096             var rm = fd.substring(n); 
13097             if (rm < 5) {
13098                 return ps[0] + '.' + r;
13099             }
13100             r*=1; // turn it into a number;
13101             r++;
13102             if (String(r).length != n) {
13103                 ps[0]*=1;
13104                 ps[0]++;
13105                 r = String(r).substring(1); // chop the end off.
13106             }
13107             
13108             return ps[0] + '.' + r;
13109              
13110         },
13111         
13112         /**
13113          * Format a number as US currency
13114          * @param {Number/String} value The numeric value to format
13115          * @return {String} The formatted currency string
13116          */
13117         usMoney : function(v){
13118             v = (Math.round((v-0)*100))/100;
13119             v = (v == Math.floor(v)) ? v + ".00" : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13120             v = String(v);
13121             var ps = v.split('.');
13122             var whole = ps[0];
13123             var sub = ps[1] ? '.'+ ps[1] : '.00';
13124             var r = /(\d+)(\d{3})/;
13125             while (r.test(whole)) {
13126                 whole = whole.replace(r, '$1' + ',' + '$2');
13127             }
13128             return "$" + whole + sub ;
13129         },
13130         
13131         /**
13132          * Parse a value into a formatted date using the specified format pattern.
13133          * @param {Mixed} value The value to format
13134          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13135          * @return {String} The formatted date string
13136          */
13137         date : function(v, format){
13138             if(!v){
13139                 return "";
13140             }
13141             if(!(v instanceof Date)){
13142                 v = new Date(Date.parse(v));
13143             }
13144             return v.dateFormat(format || "m/d/Y");
13145         },
13146
13147         /**
13148          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13149          * @param {String} format Any valid date format string
13150          * @return {Function} The date formatting function
13151          */
13152         dateRenderer : function(format){
13153             return function(v){
13154                 return Roo.util.Format.date(v, format);  
13155             };
13156         },
13157
13158         // private
13159         stripTagsRE : /<\/?[^>]+>/gi,
13160         
13161         /**
13162          * Strips all HTML tags
13163          * @param {Mixed} value The text from which to strip tags
13164          * @return {String} The stripped text
13165          */
13166         stripTags : function(v){
13167             return !v ? v : String(v).replace(this.stripTagsRE, "");
13168         }
13169     };
13170 }();/*
13171  * Based on:
13172  * Ext JS Library 1.1.1
13173  * Copyright(c) 2006-2007, Ext JS, LLC.
13174  *
13175  * Originally Released Under LGPL - original licence link has changed is not relivant.
13176  *
13177  * Fork - LGPL
13178  * <script type="text/javascript">
13179  */
13180
13181
13182  
13183
13184 /**
13185  * @class Roo.MasterTemplate
13186  * @extends Roo.Template
13187  * Provides a template that can have child templates. The syntax is:
13188 <pre><code>
13189 var t = new Roo.MasterTemplate(
13190         '&lt;select name="{name}"&gt;',
13191                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13192         '&lt;/select&gt;'
13193 );
13194 t.add('options', {value: 'foo', text: 'bar'});
13195 // or you can add multiple child elements in one shot
13196 t.addAll('options', [
13197     {value: 'foo', text: 'bar'},
13198     {value: 'foo2', text: 'bar2'},
13199     {value: 'foo3', text: 'bar3'}
13200 ]);
13201 // then append, applying the master template values
13202 t.append('my-form', {name: 'my-select'});
13203 </code></pre>
13204 * A name attribute for the child template is not required if you have only one child
13205 * template or you want to refer to them by index.
13206  */
13207 Roo.MasterTemplate = function(){
13208     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13209     this.originalHtml = this.html;
13210     var st = {};
13211     var m, re = this.subTemplateRe;
13212     re.lastIndex = 0;
13213     var subIndex = 0;
13214     while(m = re.exec(this.html)){
13215         var name = m[1], content = m[2];
13216         st[subIndex] = {
13217             name: name,
13218             index: subIndex,
13219             buffer: [],
13220             tpl : new Roo.Template(content)
13221         };
13222         if(name){
13223             st[name] = st[subIndex];
13224         }
13225         st[subIndex].tpl.compile();
13226         st[subIndex].tpl.call = this.call.createDelegate(this);
13227         subIndex++;
13228     }
13229     this.subCount = subIndex;
13230     this.subs = st;
13231 };
13232 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13233     /**
13234     * The regular expression used to match sub templates
13235     * @type RegExp
13236     * @property
13237     */
13238     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13239
13240     /**
13241      * Applies the passed values to a child template.
13242      * @param {String/Number} name (optional) The name or index of the child template
13243      * @param {Array/Object} values The values to be applied to the template
13244      * @return {MasterTemplate} this
13245      */
13246      add : function(name, values){
13247         if(arguments.length == 1){
13248             values = arguments[0];
13249             name = 0;
13250         }
13251         var s = this.subs[name];
13252         s.buffer[s.buffer.length] = s.tpl.apply(values);
13253         return this;
13254     },
13255
13256     /**
13257      * Applies all the passed values to a child template.
13258      * @param {String/Number} name (optional) The name or index of the child template
13259      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13260      * @param {Boolean} reset (optional) True to reset the template first
13261      * @return {MasterTemplate} this
13262      */
13263     fill : function(name, values, reset){
13264         var a = arguments;
13265         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13266             values = a[0];
13267             name = 0;
13268             reset = a[1];
13269         }
13270         if(reset){
13271             this.reset();
13272         }
13273         for(var i = 0, len = values.length; i < len; i++){
13274             this.add(name, values[i]);
13275         }
13276         return this;
13277     },
13278
13279     /**
13280      * Resets the template for reuse
13281      * @return {MasterTemplate} this
13282      */
13283      reset : function(){
13284         var s = this.subs;
13285         for(var i = 0; i < this.subCount; i++){
13286             s[i].buffer = [];
13287         }
13288         return this;
13289     },
13290
13291     applyTemplate : function(values){
13292         var s = this.subs;
13293         var replaceIndex = -1;
13294         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13295             return s[++replaceIndex].buffer.join("");
13296         });
13297         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13298     },
13299
13300     apply : function(){
13301         return this.applyTemplate.apply(this, arguments);
13302     },
13303
13304     compile : function(){return this;}
13305 });
13306
13307 /**
13308  * Alias for fill().
13309  * @method
13310  */
13311 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13312  /**
13313  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13314  * var tpl = Roo.MasterTemplate.from('element-id');
13315  * @param {String/HTMLElement} el
13316  * @param {Object} config
13317  * @static
13318  */
13319 Roo.MasterTemplate.from = function(el, config){
13320     el = Roo.getDom(el);
13321     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13322 };/*
13323  * Based on:
13324  * Ext JS Library 1.1.1
13325  * Copyright(c) 2006-2007, Ext JS, LLC.
13326  *
13327  * Originally Released Under LGPL - original licence link has changed is not relivant.
13328  *
13329  * Fork - LGPL
13330  * <script type="text/javascript">
13331  */
13332
13333  
13334 /**
13335  * @class Roo.util.CSS
13336  * Utility class for manipulating CSS rules
13337  * @singleton
13338  */
13339 Roo.util.CSS = function(){
13340         var rules = null;
13341         var doc = document;
13342
13343     var camelRe = /(-[a-z])/gi;
13344     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13345
13346    return {
13347    /**
13348     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
13349     * tag and appended to the HEAD of the document.
13350     * @param {String|Object} cssText The text containing the css rules
13351     * @param {String} id An id to add to the stylesheet for later removal
13352     * @return {StyleSheet}
13353     */
13354     createStyleSheet : function(cssText, id){
13355         var ss;
13356         var head = doc.getElementsByTagName("head")[0];
13357         var nrules = doc.createElement("style");
13358         nrules.setAttribute("type", "text/css");
13359         if(id){
13360             nrules.setAttribute("id", id);
13361         }
13362         if (typeof(cssText) != 'string') {
13363             // support object maps..
13364             // not sure if this a good idea.. 
13365             // perhaps it should be merged with the general css handling
13366             // and handle js style props.
13367             var cssTextNew = [];
13368             for(var n in cssText) {
13369                 var citems = [];
13370                 for(var k in cssText[n]) {
13371                     citems.push( k + ' : ' +cssText[n][k] + ';' );
13372                 }
13373                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
13374                 
13375             }
13376             cssText = cssTextNew.join("\n");
13377             
13378         }
13379        
13380        
13381        if(Roo.isIE){
13382            head.appendChild(nrules);
13383            ss = nrules.styleSheet;
13384            ss.cssText = cssText;
13385        }else{
13386            try{
13387                 nrules.appendChild(doc.createTextNode(cssText));
13388            }catch(e){
13389                nrules.cssText = cssText; 
13390            }
13391            head.appendChild(nrules);
13392            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
13393        }
13394        this.cacheStyleSheet(ss);
13395        return ss;
13396    },
13397
13398    /**
13399     * Removes a style or link tag by id
13400     * @param {String} id The id of the tag
13401     */
13402    removeStyleSheet : function(id){
13403        var existing = doc.getElementById(id);
13404        if(existing){
13405            existing.parentNode.removeChild(existing);
13406        }
13407    },
13408
13409    /**
13410     * Dynamically swaps an existing stylesheet reference for a new one
13411     * @param {String} id The id of an existing link tag to remove
13412     * @param {String} url The href of the new stylesheet to include
13413     */
13414    swapStyleSheet : function(id, url){
13415        this.removeStyleSheet(id);
13416        var ss = doc.createElement("link");
13417        ss.setAttribute("rel", "stylesheet");
13418        ss.setAttribute("type", "text/css");
13419        ss.setAttribute("id", id);
13420        ss.setAttribute("href", url);
13421        doc.getElementsByTagName("head")[0].appendChild(ss);
13422    },
13423    
13424    /**
13425     * Refresh the rule cache if you have dynamically added stylesheets
13426     * @return {Object} An object (hash) of rules indexed by selector
13427     */
13428    refreshCache : function(){
13429        return this.getRules(true);
13430    },
13431
13432    // private
13433    cacheStyleSheet : function(stylesheet){
13434        if(!rules){
13435            rules = {};
13436        }
13437        try{// try catch for cross domain access issue
13438            var ssRules = stylesheet.cssRules || stylesheet.rules;
13439            for(var j = ssRules.length-1; j >= 0; --j){
13440                rules[ssRules[j].selectorText] = ssRules[j];
13441            }
13442        }catch(e){}
13443    },
13444    
13445    /**
13446     * Gets all css rules for the document
13447     * @param {Boolean} refreshCache true to refresh the internal cache
13448     * @return {Object} An object (hash) of rules indexed by selector
13449     */
13450    getRules : function(refreshCache){
13451                 if(rules == null || refreshCache){
13452                         rules = {};
13453                         var ds = doc.styleSheets;
13454                         for(var i =0, len = ds.length; i < len; i++){
13455                             try{
13456                         this.cacheStyleSheet(ds[i]);
13457                     }catch(e){} 
13458                 }
13459                 }
13460                 return rules;
13461         },
13462         
13463         /**
13464     * Gets an an individual CSS rule by selector(s)
13465     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
13466     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
13467     * @return {CSSRule} The CSS rule or null if one is not found
13468     */
13469    getRule : function(selector, refreshCache){
13470                 var rs = this.getRules(refreshCache);
13471                 if(!(selector instanceof Array)){
13472                     return rs[selector];
13473                 }
13474                 for(var i = 0; i < selector.length; i++){
13475                         if(rs[selector[i]]){
13476                                 return rs[selector[i]];
13477                         }
13478                 }
13479                 return null;
13480         },
13481         
13482         
13483         /**
13484     * Updates a rule property
13485     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
13486     * @param {String} property The css property
13487     * @param {String} value The new value for the property
13488     * @return {Boolean} true If a rule was found and updated
13489     */
13490    updateRule : function(selector, property, value){
13491                 if(!(selector instanceof Array)){
13492                         var rule = this.getRule(selector);
13493                         if(rule){
13494                                 rule.style[property.replace(camelRe, camelFn)] = value;
13495                                 return true;
13496                         }
13497                 }else{
13498                         for(var i = 0; i < selector.length; i++){
13499                                 if(this.updateRule(selector[i], property, value)){
13500                                         return true;
13501                                 }
13502                         }
13503                 }
13504                 return false;
13505         }
13506    };   
13507 }();/*
13508  * Based on:
13509  * Ext JS Library 1.1.1
13510  * Copyright(c) 2006-2007, Ext JS, LLC.
13511  *
13512  * Originally Released Under LGPL - original licence link has changed is not relivant.
13513  *
13514  * Fork - LGPL
13515  * <script type="text/javascript">
13516  */
13517
13518  
13519
13520 /**
13521  * @class Roo.util.ClickRepeater
13522  * @extends Roo.util.Observable
13523  * 
13524  * A wrapper class which can be applied to any element. Fires a "click" event while the
13525  * mouse is pressed. The interval between firings may be specified in the config but
13526  * defaults to 10 milliseconds.
13527  * 
13528  * Optionally, a CSS class may be applied to the element during the time it is pressed.
13529  * 
13530  * @cfg {String/HTMLElement/Element} el The element to act as a button.
13531  * @cfg {Number} delay The initial delay before the repeating event begins firing.
13532  * Similar to an autorepeat key delay.
13533  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
13534  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
13535  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
13536  *           "interval" and "delay" are ignored. "immediate" is honored.
13537  * @cfg {Boolean} preventDefault True to prevent the default click event
13538  * @cfg {Boolean} stopDefault True to stop the default click event
13539  * 
13540  * @history
13541  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
13542  *     2007-02-02 jvs Renamed to ClickRepeater
13543  *   2007-02-03 jvs Modifications for FF Mac and Safari 
13544  *
13545  *  @constructor
13546  * @param {String/HTMLElement/Element} el The element to listen on
13547  * @param {Object} config
13548  **/
13549 Roo.util.ClickRepeater = function(el, config)
13550 {
13551     this.el = Roo.get(el);
13552     this.el.unselectable();
13553
13554     Roo.apply(this, config);
13555
13556     this.addEvents({
13557     /**
13558      * @event mousedown
13559      * Fires when the mouse button is depressed.
13560      * @param {Roo.util.ClickRepeater} this
13561      */
13562         "mousedown" : true,
13563     /**
13564      * @event click
13565      * Fires on a specified interval during the time the element is pressed.
13566      * @param {Roo.util.ClickRepeater} this
13567      */
13568         "click" : true,
13569     /**
13570      * @event mouseup
13571      * Fires when the mouse key is released.
13572      * @param {Roo.util.ClickRepeater} this
13573      */
13574         "mouseup" : true
13575     });
13576
13577     this.el.on("mousedown", this.handleMouseDown, this);
13578     if(this.preventDefault || this.stopDefault){
13579         this.el.on("click", function(e){
13580             if(this.preventDefault){
13581                 e.preventDefault();
13582             }
13583             if(this.stopDefault){
13584                 e.stopEvent();
13585             }
13586         }, this);
13587     }
13588
13589     // allow inline handler
13590     if(this.handler){
13591         this.on("click", this.handler,  this.scope || this);
13592     }
13593
13594     Roo.util.ClickRepeater.superclass.constructor.call(this);
13595 };
13596
13597 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
13598     interval : 20,
13599     delay: 250,
13600     preventDefault : true,
13601     stopDefault : false,
13602     timer : 0,
13603
13604     // private
13605     handleMouseDown : function(){
13606         clearTimeout(this.timer);
13607         this.el.blur();
13608         if(this.pressClass){
13609             this.el.addClass(this.pressClass);
13610         }
13611         this.mousedownTime = new Date();
13612
13613         Roo.get(document).on("mouseup", this.handleMouseUp, this);
13614         this.el.on("mouseout", this.handleMouseOut, this);
13615
13616         this.fireEvent("mousedown", this);
13617         this.fireEvent("click", this);
13618         
13619         this.timer = this.click.defer(this.delay || this.interval, this);
13620     },
13621
13622     // private
13623     click : function(){
13624         this.fireEvent("click", this);
13625         this.timer = this.click.defer(this.getInterval(), this);
13626     },
13627
13628     // private
13629     getInterval: function(){
13630         if(!this.accelerate){
13631             return this.interval;
13632         }
13633         var pressTime = this.mousedownTime.getElapsed();
13634         if(pressTime < 500){
13635             return 400;
13636         }else if(pressTime < 1700){
13637             return 320;
13638         }else if(pressTime < 2600){
13639             return 250;
13640         }else if(pressTime < 3500){
13641             return 180;
13642         }else if(pressTime < 4400){
13643             return 140;
13644         }else if(pressTime < 5300){
13645             return 80;
13646         }else if(pressTime < 6200){
13647             return 50;
13648         }else{
13649             return 10;
13650         }
13651     },
13652
13653     // private
13654     handleMouseOut : function(){
13655         clearTimeout(this.timer);
13656         if(this.pressClass){
13657             this.el.removeClass(this.pressClass);
13658         }
13659         this.el.on("mouseover", this.handleMouseReturn, this);
13660     },
13661
13662     // private
13663     handleMouseReturn : function(){
13664         this.el.un("mouseover", this.handleMouseReturn);
13665         if(this.pressClass){
13666             this.el.addClass(this.pressClass);
13667         }
13668         this.click();
13669     },
13670
13671     // private
13672     handleMouseUp : function(){
13673         clearTimeout(this.timer);
13674         this.el.un("mouseover", this.handleMouseReturn);
13675         this.el.un("mouseout", this.handleMouseOut);
13676         Roo.get(document).un("mouseup", this.handleMouseUp);
13677         this.el.removeClass(this.pressClass);
13678         this.fireEvent("mouseup", this);
13679     }
13680 });/*
13681  * Based on:
13682  * Ext JS Library 1.1.1
13683  * Copyright(c) 2006-2007, Ext JS, LLC.
13684  *
13685  * Originally Released Under LGPL - original licence link has changed is not relivant.
13686  *
13687  * Fork - LGPL
13688  * <script type="text/javascript">
13689  */
13690
13691  
13692 /**
13693  * @class Roo.KeyNav
13694  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
13695  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
13696  * way to implement custom navigation schemes for any UI component.</p>
13697  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
13698  * pageUp, pageDown, del, home, end.  Usage:</p>
13699  <pre><code>
13700 var nav = new Roo.KeyNav("my-element", {
13701     "left" : function(e){
13702         this.moveLeft(e.ctrlKey);
13703     },
13704     "right" : function(e){
13705         this.moveRight(e.ctrlKey);
13706     },
13707     "enter" : function(e){
13708         this.save();
13709     },
13710     scope : this
13711 });
13712 </code></pre>
13713  * @constructor
13714  * @param {String/HTMLElement/Roo.Element} el The element to bind to
13715  * @param {Object} config The config
13716  */
13717 Roo.KeyNav = function(el, config){
13718     this.el = Roo.get(el);
13719     Roo.apply(this, config);
13720     if(!this.disabled){
13721         this.disabled = true;
13722         this.enable();
13723     }
13724 };
13725
13726 Roo.KeyNav.prototype = {
13727     /**
13728      * @cfg {Boolean} disabled
13729      * True to disable this KeyNav instance (defaults to false)
13730      */
13731     disabled : false,
13732     /**
13733      * @cfg {String} defaultEventAction
13734      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
13735      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
13736      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
13737      */
13738     defaultEventAction: "stopEvent",
13739     /**
13740      * @cfg {Boolean} forceKeyDown
13741      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
13742      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
13743      * handle keydown instead of keypress.
13744      */
13745     forceKeyDown : false,
13746
13747     // private
13748     prepareEvent : function(e){
13749         var k = e.getKey();
13750         var h = this.keyToHandler[k];
13751         //if(h && this[h]){
13752         //    e.stopPropagation();
13753         //}
13754         if(Roo.isSafari && h && k >= 37 && k <= 40){
13755             e.stopEvent();
13756         }
13757     },
13758
13759     // private
13760     relay : function(e){
13761         var k = e.getKey();
13762         var h = this.keyToHandler[k];
13763         if(h && this[h]){
13764             if(this.doRelay(e, this[h], h) !== true){
13765                 e[this.defaultEventAction]();
13766             }
13767         }
13768     },
13769
13770     // private
13771     doRelay : function(e, h, hname){
13772         return h.call(this.scope || this, e);
13773     },
13774
13775     // possible handlers
13776     enter : false,
13777     left : false,
13778     right : false,
13779     up : false,
13780     down : false,
13781     tab : false,
13782     esc : false,
13783     pageUp : false,
13784     pageDown : false,
13785     del : false,
13786     home : false,
13787     end : false,
13788
13789     // quick lookup hash
13790     keyToHandler : {
13791         37 : "left",
13792         39 : "right",
13793         38 : "up",
13794         40 : "down",
13795         33 : "pageUp",
13796         34 : "pageDown",
13797         46 : "del",
13798         36 : "home",
13799         35 : "end",
13800         13 : "enter",
13801         27 : "esc",
13802         9  : "tab"
13803     },
13804
13805         /**
13806          * Enable this KeyNav
13807          */
13808         enable: function(){
13809                 if(this.disabled){
13810             // ie won't do special keys on keypress, no one else will repeat keys with keydown
13811             // the EventObject will normalize Safari automatically
13812             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
13813                 this.el.on("keydown", this.relay,  this);
13814             }else{
13815                 this.el.on("keydown", this.prepareEvent,  this);
13816                 this.el.on("keypress", this.relay,  this);
13817             }
13818                     this.disabled = false;
13819                 }
13820         },
13821
13822         /**
13823          * Disable this KeyNav
13824          */
13825         disable: function(){
13826                 if(!this.disabled){
13827                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
13828                 this.el.un("keydown", this.relay);
13829             }else{
13830                 this.el.un("keydown", this.prepareEvent);
13831                 this.el.un("keypress", this.relay);
13832             }
13833                     this.disabled = true;
13834                 }
13835         }
13836 };/*
13837  * Based on:
13838  * Ext JS Library 1.1.1
13839  * Copyright(c) 2006-2007, Ext JS, LLC.
13840  *
13841  * Originally Released Under LGPL - original licence link has changed is not relivant.
13842  *
13843  * Fork - LGPL
13844  * <script type="text/javascript">
13845  */
13846
13847  
13848 /**
13849  * @class Roo.KeyMap
13850  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
13851  * The constructor accepts the same config object as defined by {@link #addBinding}.
13852  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
13853  * combination it will call the function with this signature (if the match is a multi-key
13854  * combination the callback will still be called only once): (String key, Roo.EventObject e)
13855  * A KeyMap can also handle a string representation of keys.<br />
13856  * Usage:
13857  <pre><code>
13858 // map one key by key code
13859 var map = new Roo.KeyMap("my-element", {
13860     key: 13, // or Roo.EventObject.ENTER
13861     fn: myHandler,
13862     scope: myObject
13863 });
13864
13865 // map multiple keys to one action by string
13866 var map = new Roo.KeyMap("my-element", {
13867     key: "a\r\n\t",
13868     fn: myHandler,
13869     scope: myObject
13870 });
13871
13872 // map multiple keys to multiple actions by strings and array of codes
13873 var map = new Roo.KeyMap("my-element", [
13874     {
13875         key: [10,13],
13876         fn: function(){ alert("Return was pressed"); }
13877     }, {
13878         key: "abc",
13879         fn: function(){ alert('a, b or c was pressed'); }
13880     }, {
13881         key: "\t",
13882         ctrl:true,
13883         shift:true,
13884         fn: function(){ alert('Control + shift + tab was pressed.'); }
13885     }
13886 ]);
13887 </code></pre>
13888  * <b>Note: A KeyMap starts enabled</b>
13889  * @constructor
13890  * @param {String/HTMLElement/Roo.Element} el The element to bind to
13891  * @param {Object} config The config (see {@link #addBinding})
13892  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
13893  */
13894 Roo.KeyMap = function(el, config, eventName){
13895     this.el  = Roo.get(el);
13896     this.eventName = eventName || "keydown";
13897     this.bindings = [];
13898     if(config){
13899         this.addBinding(config);
13900     }
13901     this.enable();
13902 };
13903
13904 Roo.KeyMap.prototype = {
13905     /**
13906      * True to stop the event from bubbling and prevent the default browser action if the
13907      * key was handled by the KeyMap (defaults to false)
13908      * @type Boolean
13909      */
13910     stopEvent : false,
13911
13912     /**
13913      * Add a new binding to this KeyMap. The following config object properties are supported:
13914      * <pre>
13915 Property    Type             Description
13916 ----------  ---------------  ----------------------------------------------------------------------
13917 key         String/Array     A single keycode or an array of keycodes to handle
13918 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
13919 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
13920 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
13921 fn          Function         The function to call when KeyMap finds the expected key combination
13922 scope       Object           The scope of the callback function
13923 </pre>
13924      *
13925      * Usage:
13926      * <pre><code>
13927 // Create a KeyMap
13928 var map = new Roo.KeyMap(document, {
13929     key: Roo.EventObject.ENTER,
13930     fn: handleKey,
13931     scope: this
13932 });
13933
13934 //Add a new binding to the existing KeyMap later
13935 map.addBinding({
13936     key: 'abc',
13937     shift: true,
13938     fn: handleKey,
13939     scope: this
13940 });
13941 </code></pre>
13942      * @param {Object/Array} config A single KeyMap config or an array of configs
13943      */
13944         addBinding : function(config){
13945         if(config instanceof Array){
13946             for(var i = 0, len = config.length; i < len; i++){
13947                 this.addBinding(config[i]);
13948             }
13949             return;
13950         }
13951         var keyCode = config.key,
13952             shift = config.shift, 
13953             ctrl = config.ctrl, 
13954             alt = config.alt,
13955             fn = config.fn,
13956             scope = config.scope;
13957         if(typeof keyCode == "string"){
13958             var ks = [];
13959             var keyString = keyCode.toUpperCase();
13960             for(var j = 0, len = keyString.length; j < len; j++){
13961                 ks.push(keyString.charCodeAt(j));
13962             }
13963             keyCode = ks;
13964         }
13965         var keyArray = keyCode instanceof Array;
13966         var handler = function(e){
13967             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
13968                 var k = e.getKey();
13969                 if(keyArray){
13970                     for(var i = 0, len = keyCode.length; i < len; i++){
13971                         if(keyCode[i] == k){
13972                           if(this.stopEvent){
13973                               e.stopEvent();
13974                           }
13975                           fn.call(scope || window, k, e);
13976                           return;
13977                         }
13978                     }
13979                 }else{
13980                     if(k == keyCode){
13981                         if(this.stopEvent){
13982                            e.stopEvent();
13983                         }
13984                         fn.call(scope || window, k, e);
13985                     }
13986                 }
13987             }
13988         };
13989         this.bindings.push(handler);  
13990         },
13991
13992     /**
13993      * Shorthand for adding a single key listener
13994      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
13995      * following options:
13996      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
13997      * @param {Function} fn The function to call
13998      * @param {Object} scope (optional) The scope of the function
13999      */
14000     on : function(key, fn, scope){
14001         var keyCode, shift, ctrl, alt;
14002         if(typeof key == "object" && !(key instanceof Array)){
14003             keyCode = key.key;
14004             shift = key.shift;
14005             ctrl = key.ctrl;
14006             alt = key.alt;
14007         }else{
14008             keyCode = key;
14009         }
14010         this.addBinding({
14011             key: keyCode,
14012             shift: shift,
14013             ctrl: ctrl,
14014             alt: alt,
14015             fn: fn,
14016             scope: scope
14017         })
14018     },
14019
14020     // private
14021     handleKeyDown : function(e){
14022             if(this.enabled){ //just in case
14023             var b = this.bindings;
14024             for(var i = 0, len = b.length; i < len; i++){
14025                 b[i].call(this, e);
14026             }
14027             }
14028         },
14029         
14030         /**
14031          * Returns true if this KeyMap is enabled
14032          * @return {Boolean} 
14033          */
14034         isEnabled : function(){
14035             return this.enabled;  
14036         },
14037         
14038         /**
14039          * Enables this KeyMap
14040          */
14041         enable: function(){
14042                 if(!this.enabled){
14043                     this.el.on(this.eventName, this.handleKeyDown, this);
14044                     this.enabled = true;
14045                 }
14046         },
14047
14048         /**
14049          * Disable this KeyMap
14050          */
14051         disable: function(){
14052                 if(this.enabled){
14053                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14054                     this.enabled = false;
14055                 }
14056         }
14057 };/*
14058  * Based on:
14059  * Ext JS Library 1.1.1
14060  * Copyright(c) 2006-2007, Ext JS, LLC.
14061  *
14062  * Originally Released Under LGPL - original licence link has changed is not relivant.
14063  *
14064  * Fork - LGPL
14065  * <script type="text/javascript">
14066  */
14067
14068  
14069 /**
14070  * @class Roo.util.TextMetrics
14071  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14072  * wide, in pixels, a given block of text will be.
14073  * @singleton
14074  */
14075 Roo.util.TextMetrics = function(){
14076     var shared;
14077     return {
14078         /**
14079          * Measures the size of the specified text
14080          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14081          * that can affect the size of the rendered text
14082          * @param {String} text The text to measure
14083          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14084          * in order to accurately measure the text height
14085          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14086          */
14087         measure : function(el, text, fixedWidth){
14088             if(!shared){
14089                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14090             }
14091             shared.bind(el);
14092             shared.setFixedWidth(fixedWidth || 'auto');
14093             return shared.getSize(text);
14094         },
14095
14096         /**
14097          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14098          * the overhead of multiple calls to initialize the style properties on each measurement.
14099          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14100          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14101          * in order to accurately measure the text height
14102          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14103          */
14104         createInstance : function(el, fixedWidth){
14105             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14106         }
14107     };
14108 }();
14109
14110  
14111
14112 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14113     var ml = new Roo.Element(document.createElement('div'));
14114     document.body.appendChild(ml.dom);
14115     ml.position('absolute');
14116     ml.setLeftTop(-1000, -1000);
14117     ml.hide();
14118
14119     if(fixedWidth){
14120         ml.setWidth(fixedWidth);
14121     }
14122      
14123     var instance = {
14124         /**
14125          * Returns the size of the specified text based on the internal element's style and width properties
14126          * @memberOf Roo.util.TextMetrics.Instance#
14127          * @param {String} text The text to measure
14128          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14129          */
14130         getSize : function(text){
14131             ml.update(text);
14132             var s = ml.getSize();
14133             ml.update('');
14134             return s;
14135         },
14136
14137         /**
14138          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14139          * that can affect the size of the rendered text
14140          * @memberOf Roo.util.TextMetrics.Instance#
14141          * @param {String/HTMLElement} el The element, dom node or id
14142          */
14143         bind : function(el){
14144             ml.setStyle(
14145                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14146             );
14147         },
14148
14149         /**
14150          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14151          * to set a fixed width in order to accurately measure the text height.
14152          * @memberOf Roo.util.TextMetrics.Instance#
14153          * @param {Number} width The width to set on the element
14154          */
14155         setFixedWidth : function(width){
14156             ml.setWidth(width);
14157         },
14158
14159         /**
14160          * Returns the measured width of the specified text
14161          * @memberOf Roo.util.TextMetrics.Instance#
14162          * @param {String} text The text to measure
14163          * @return {Number} width The width in pixels
14164          */
14165         getWidth : function(text){
14166             ml.dom.style.width = 'auto';
14167             return this.getSize(text).width;
14168         },
14169
14170         /**
14171          * Returns the measured height of the specified text.  For multiline text, be sure to call
14172          * {@link #setFixedWidth} if necessary.
14173          * @memberOf Roo.util.TextMetrics.Instance#
14174          * @param {String} text The text to measure
14175          * @return {Number} height The height in pixels
14176          */
14177         getHeight : function(text){
14178             return this.getSize(text).height;
14179         }
14180     };
14181
14182     instance.bind(bindTo);
14183
14184     return instance;
14185 };
14186
14187 // backwards compat
14188 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14189  * Based on:
14190  * Ext JS Library 1.1.1
14191  * Copyright(c) 2006-2007, Ext JS, LLC.
14192  *
14193  * Originally Released Under LGPL - original licence link has changed is not relivant.
14194  *
14195  * Fork - LGPL
14196  * <script type="text/javascript">
14197  */
14198
14199 /**
14200  * @class Roo.state.Provider
14201  * Abstract base class for state provider implementations. This class provides methods
14202  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14203  * Provider interface.
14204  */
14205 Roo.state.Provider = function(){
14206     /**
14207      * @event statechange
14208      * Fires when a state change occurs.
14209      * @param {Provider} this This state provider
14210      * @param {String} key The state key which was changed
14211      * @param {String} value The encoded value for the state
14212      */
14213     this.addEvents({
14214         "statechange": true
14215     });
14216     this.state = {};
14217     Roo.state.Provider.superclass.constructor.call(this);
14218 };
14219 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14220     /**
14221      * Returns the current value for a key
14222      * @param {String} name The key name
14223      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14224      * @return {Mixed} The state data
14225      */
14226     get : function(name, defaultValue){
14227         return typeof this.state[name] == "undefined" ?
14228             defaultValue : this.state[name];
14229     },
14230     
14231     /**
14232      * Clears a value from the state
14233      * @param {String} name The key name
14234      */
14235     clear : function(name){
14236         delete this.state[name];
14237         this.fireEvent("statechange", this, name, null);
14238     },
14239     
14240     /**
14241      * Sets the value for a key
14242      * @param {String} name The key name
14243      * @param {Mixed} value The value to set
14244      */
14245     set : function(name, value){
14246         this.state[name] = value;
14247         this.fireEvent("statechange", this, name, value);
14248     },
14249     
14250     /**
14251      * Decodes a string previously encoded with {@link #encodeValue}.
14252      * @param {String} value The value to decode
14253      * @return {Mixed} The decoded value
14254      */
14255     decodeValue : function(cookie){
14256         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14257         var matches = re.exec(unescape(cookie));
14258         if(!matches || !matches[1]) return; // non state cookie
14259         var type = matches[1];
14260         var v = matches[2];
14261         switch(type){
14262             case "n":
14263                 return parseFloat(v);
14264             case "d":
14265                 return new Date(Date.parse(v));
14266             case "b":
14267                 return (v == "1");
14268             case "a":
14269                 var all = [];
14270                 var values = v.split("^");
14271                 for(var i = 0, len = values.length; i < len; i++){
14272                     all.push(this.decodeValue(values[i]));
14273                 }
14274                 return all;
14275            case "o":
14276                 var all = {};
14277                 var values = v.split("^");
14278                 for(var i = 0, len = values.length; i < len; i++){
14279                     var kv = values[i].split("=");
14280                     all[kv[0]] = this.decodeValue(kv[1]);
14281                 }
14282                 return all;
14283            default:
14284                 return v;
14285         }
14286     },
14287     
14288     /**
14289      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14290      * @param {Mixed} value The value to encode
14291      * @return {String} The encoded value
14292      */
14293     encodeValue : function(v){
14294         var enc;
14295         if(typeof v == "number"){
14296             enc = "n:" + v;
14297         }else if(typeof v == "boolean"){
14298             enc = "b:" + (v ? "1" : "0");
14299         }else if(v instanceof Date){
14300             enc = "d:" + v.toGMTString();
14301         }else if(v instanceof Array){
14302             var flat = "";
14303             for(var i = 0, len = v.length; i < len; i++){
14304                 flat += this.encodeValue(v[i]);
14305                 if(i != len-1) flat += "^";
14306             }
14307             enc = "a:" + flat;
14308         }else if(typeof v == "object"){
14309             var flat = "";
14310             for(var key in v){
14311                 if(typeof v[key] != "function"){
14312                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14313                 }
14314             }
14315             enc = "o:" + flat.substring(0, flat.length-1);
14316         }else{
14317             enc = "s:" + v;
14318         }
14319         return escape(enc);        
14320     }
14321 });
14322
14323 /*
14324  * Based on:
14325  * Ext JS Library 1.1.1
14326  * Copyright(c) 2006-2007, Ext JS, LLC.
14327  *
14328  * Originally Released Under LGPL - original licence link has changed is not relivant.
14329  *
14330  * Fork - LGPL
14331  * <script type="text/javascript">
14332  */
14333 /**
14334  * @class Roo.state.Manager
14335  * This is the global state manager. By default all components that are "state aware" check this class
14336  * for state information if you don't pass them a custom state provider. In order for this class
14337  * to be useful, it must be initialized with a provider when your application initializes.
14338  <pre><code>
14339 // in your initialization function
14340 init : function(){
14341    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14342    ...
14343    // supposed you have a {@link Roo.BorderLayout}
14344    var layout = new Roo.BorderLayout(...);
14345    layout.restoreState();
14346    // or a {Roo.BasicDialog}
14347    var dialog = new Roo.BasicDialog(...);
14348    dialog.restoreState();
14349  </code></pre>
14350  * @singleton
14351  */
14352 Roo.state.Manager = function(){
14353     var provider = new Roo.state.Provider();
14354     
14355     return {
14356         /**
14357          * Configures the default state provider for your application
14358          * @param {Provider} stateProvider The state provider to set
14359          */
14360         setProvider : function(stateProvider){
14361             provider = stateProvider;
14362         },
14363         
14364         /**
14365          * Returns the current value for a key
14366          * @param {String} name The key name
14367          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
14368          * @return {Mixed} The state data
14369          */
14370         get : function(key, defaultValue){
14371             return provider.get(key, defaultValue);
14372         },
14373         
14374         /**
14375          * Sets the value for a key
14376          * @param {String} name The key name
14377          * @param {Mixed} value The state data
14378          */
14379          set : function(key, value){
14380             provider.set(key, value);
14381         },
14382         
14383         /**
14384          * Clears a value from the state
14385          * @param {String} name The key name
14386          */
14387         clear : function(key){
14388             provider.clear(key);
14389         },
14390         
14391         /**
14392          * Gets the currently configured state provider
14393          * @return {Provider} The state provider
14394          */
14395         getProvider : function(){
14396             return provider;
14397         }
14398     };
14399 }();
14400 /*
14401  * Based on:
14402  * Ext JS Library 1.1.1
14403  * Copyright(c) 2006-2007, Ext JS, LLC.
14404  *
14405  * Originally Released Under LGPL - original licence link has changed is not relivant.
14406  *
14407  * Fork - LGPL
14408  * <script type="text/javascript">
14409  */
14410 /**
14411  * @class Roo.state.CookieProvider
14412  * @extends Roo.state.Provider
14413  * The default Provider implementation which saves state via cookies.
14414  * <br />Usage:
14415  <pre><code>
14416    var cp = new Roo.state.CookieProvider({
14417        path: "/cgi-bin/",
14418        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
14419        domain: "roojs.com"
14420    })
14421    Roo.state.Manager.setProvider(cp);
14422  </code></pre>
14423  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
14424  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
14425  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
14426  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
14427  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
14428  * domain the page is running on including the 'www' like 'www.roojs.com')
14429  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
14430  * @constructor
14431  * Create a new CookieProvider
14432  * @param {Object} config The configuration object
14433  */
14434 Roo.state.CookieProvider = function(config){
14435     Roo.state.CookieProvider.superclass.constructor.call(this);
14436     this.path = "/";
14437     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
14438     this.domain = null;
14439     this.secure = false;
14440     Roo.apply(this, config);
14441     this.state = this.readCookies();
14442 };
14443
14444 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
14445     // private
14446     set : function(name, value){
14447         if(typeof value == "undefined" || value === null){
14448             this.clear(name);
14449             return;
14450         }
14451         this.setCookie(name, value);
14452         Roo.state.CookieProvider.superclass.set.call(this, name, value);
14453     },
14454
14455     // private
14456     clear : function(name){
14457         this.clearCookie(name);
14458         Roo.state.CookieProvider.superclass.clear.call(this, name);
14459     },
14460
14461     // private
14462     readCookies : function(){
14463         var cookies = {};
14464         var c = document.cookie + ";";
14465         var re = /\s?(.*?)=(.*?);/g;
14466         var matches;
14467         while((matches = re.exec(c)) != null){
14468             var name = matches[1];
14469             var value = matches[2];
14470             if(name && name.substring(0,3) == "ys-"){
14471                 cookies[name.substr(3)] = this.decodeValue(value);
14472             }
14473         }
14474         return cookies;
14475     },
14476
14477     // private
14478     setCookie : function(name, value){
14479         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
14480            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
14481            ((this.path == null) ? "" : ("; path=" + this.path)) +
14482            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14483            ((this.secure == true) ? "; secure" : "");
14484     },
14485
14486     // private
14487     clearCookie : function(name){
14488         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
14489            ((this.path == null) ? "" : ("; path=" + this.path)) +
14490            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14491            ((this.secure == true) ? "; secure" : "");
14492     }
14493 });/*
14494  * Based on:
14495  * Ext JS Library 1.1.1
14496  * Copyright(c) 2006-2007, Ext JS, LLC.
14497  *
14498  * Originally Released Under LGPL - original licence link has changed is not relivant.
14499  *
14500  * Fork - LGPL
14501  * <script type="text/javascript">
14502  */
14503
14504
14505
14506 /*
14507  * These classes are derivatives of the similarly named classes in the YUI Library.
14508  * The original license:
14509  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
14510  * Code licensed under the BSD License:
14511  * http://developer.yahoo.net/yui/license.txt
14512  */
14513
14514 (function() {
14515
14516 var Event=Roo.EventManager;
14517 var Dom=Roo.lib.Dom;
14518
14519 /**
14520  * @class Roo.dd.DragDrop
14521  * @extends Roo.util.Observable
14522  * Defines the interface and base operation of items that that can be
14523  * dragged or can be drop targets.  It was designed to be extended, overriding
14524  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
14525  * Up to three html elements can be associated with a DragDrop instance:
14526  * <ul>
14527  * <li>linked element: the element that is passed into the constructor.
14528  * This is the element which defines the boundaries for interaction with
14529  * other DragDrop objects.</li>
14530  * <li>handle element(s): The drag operation only occurs if the element that
14531  * was clicked matches a handle element.  By default this is the linked
14532  * element, but there are times that you will want only a portion of the
14533  * linked element to initiate the drag operation, and the setHandleElId()
14534  * method provides a way to define this.</li>
14535  * <li>drag element: this represents the element that would be moved along
14536  * with the cursor during a drag operation.  By default, this is the linked
14537  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
14538  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
14539  * </li>
14540  * </ul>
14541  * This class should not be instantiated until the onload event to ensure that
14542  * the associated elements are available.
14543  * The following would define a DragDrop obj that would interact with any
14544  * other DragDrop obj in the "group1" group:
14545  * <pre>
14546  *  dd = new Roo.dd.DragDrop("div1", "group1");
14547  * </pre>
14548  * Since none of the event handlers have been implemented, nothing would
14549  * actually happen if you were to run the code above.  Normally you would
14550  * override this class or one of the default implementations, but you can
14551  * also override the methods you want on an instance of the class...
14552  * <pre>
14553  *  dd.onDragDrop = function(e, id) {
14554  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
14555  *  }
14556  * </pre>
14557  * @constructor
14558  * @param {String} id of the element that is linked to this instance
14559  * @param {String} sGroup the group of related DragDrop objects
14560  * @param {object} config an object containing configurable attributes
14561  *                Valid properties for DragDrop:
14562  *                    padding, isTarget, maintainOffset, primaryButtonOnly
14563  */
14564 Roo.dd.DragDrop = function(id, sGroup, config) {
14565     if (id) {
14566         this.init(id, sGroup, config);
14567     }
14568     
14569 };
14570
14571 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
14572
14573     /**
14574      * The id of the element associated with this object.  This is what we
14575      * refer to as the "linked element" because the size and position of
14576      * this element is used to determine when the drag and drop objects have
14577      * interacted.
14578      * @property id
14579      * @type String
14580      */
14581     id: null,
14582
14583     /**
14584      * Configuration attributes passed into the constructor
14585      * @property config
14586      * @type object
14587      */
14588     config: null,
14589
14590     /**
14591      * The id of the element that will be dragged.  By default this is same
14592      * as the linked element , but could be changed to another element. Ex:
14593      * Roo.dd.DDProxy
14594      * @property dragElId
14595      * @type String
14596      * @private
14597      */
14598     dragElId: null,
14599
14600     /**
14601      * the id of the element that initiates the drag operation.  By default
14602      * this is the linked element, but could be changed to be a child of this
14603      * element.  This lets us do things like only starting the drag when the
14604      * header element within the linked html element is clicked.
14605      * @property handleElId
14606      * @type String
14607      * @private
14608      */
14609     handleElId: null,
14610
14611     /**
14612      * An associative array of HTML tags that will be ignored if clicked.
14613      * @property invalidHandleTypes
14614      * @type {string: string}
14615      */
14616     invalidHandleTypes: null,
14617
14618     /**
14619      * An associative array of ids for elements that will be ignored if clicked
14620      * @property invalidHandleIds
14621      * @type {string: string}
14622      */
14623     invalidHandleIds: null,
14624
14625     /**
14626      * An indexted array of css class names for elements that will be ignored
14627      * if clicked.
14628      * @property invalidHandleClasses
14629      * @type string[]
14630      */
14631     invalidHandleClasses: null,
14632
14633     /**
14634      * The linked element's absolute X position at the time the drag was
14635      * started
14636      * @property startPageX
14637      * @type int
14638      * @private
14639      */
14640     startPageX: 0,
14641
14642     /**
14643      * The linked element's absolute X position at the time the drag was
14644      * started
14645      * @property startPageY
14646      * @type int
14647      * @private
14648      */
14649     startPageY: 0,
14650
14651     /**
14652      * The group defines a logical collection of DragDrop objects that are
14653      * related.  Instances only get events when interacting with other
14654      * DragDrop object in the same group.  This lets us define multiple
14655      * groups using a single DragDrop subclass if we want.
14656      * @property groups
14657      * @type {string: string}
14658      */
14659     groups: null,
14660
14661     /**
14662      * Individual drag/drop instances can be locked.  This will prevent
14663      * onmousedown start drag.
14664      * @property locked
14665      * @type boolean
14666      * @private
14667      */
14668     locked: false,
14669
14670     /**
14671      * Lock this instance
14672      * @method lock
14673      */
14674     lock: function() { this.locked = true; },
14675
14676     /**
14677      * Unlock this instace
14678      * @method unlock
14679      */
14680     unlock: function() { this.locked = false; },
14681
14682     /**
14683      * By default, all insances can be a drop target.  This can be disabled by
14684      * setting isTarget to false.
14685      * @method isTarget
14686      * @type boolean
14687      */
14688     isTarget: true,
14689
14690     /**
14691      * The padding configured for this drag and drop object for calculating
14692      * the drop zone intersection with this object.
14693      * @method padding
14694      * @type int[]
14695      */
14696     padding: null,
14697
14698     /**
14699      * Cached reference to the linked element
14700      * @property _domRef
14701      * @private
14702      */
14703     _domRef: null,
14704
14705     /**
14706      * Internal typeof flag
14707      * @property __ygDragDrop
14708      * @private
14709      */
14710     __ygDragDrop: true,
14711
14712     /**
14713      * Set to true when horizontal contraints are applied
14714      * @property constrainX
14715      * @type boolean
14716      * @private
14717      */
14718     constrainX: false,
14719
14720     /**
14721      * Set to true when vertical contraints are applied
14722      * @property constrainY
14723      * @type boolean
14724      * @private
14725      */
14726     constrainY: false,
14727
14728     /**
14729      * The left constraint
14730      * @property minX
14731      * @type int
14732      * @private
14733      */
14734     minX: 0,
14735
14736     /**
14737      * The right constraint
14738      * @property maxX
14739      * @type int
14740      * @private
14741      */
14742     maxX: 0,
14743
14744     /**
14745      * The up constraint
14746      * @property minY
14747      * @type int
14748      * @type int
14749      * @private
14750      */
14751     minY: 0,
14752
14753     /**
14754      * The down constraint
14755      * @property maxY
14756      * @type int
14757      * @private
14758      */
14759     maxY: 0,
14760
14761     /**
14762      * Maintain offsets when we resetconstraints.  Set to true when you want
14763      * the position of the element relative to its parent to stay the same
14764      * when the page changes
14765      *
14766      * @property maintainOffset
14767      * @type boolean
14768      */
14769     maintainOffset: false,
14770
14771     /**
14772      * Array of pixel locations the element will snap to if we specified a
14773      * horizontal graduation/interval.  This array is generated automatically
14774      * when you define a tick interval.
14775      * @property xTicks
14776      * @type int[]
14777      */
14778     xTicks: null,
14779
14780     /**
14781      * Array of pixel locations the element will snap to if we specified a
14782      * vertical graduation/interval.  This array is generated automatically
14783      * when you define a tick interval.
14784      * @property yTicks
14785      * @type int[]
14786      */
14787     yTicks: null,
14788
14789     /**
14790      * By default the drag and drop instance will only respond to the primary
14791      * button click (left button for a right-handed mouse).  Set to true to
14792      * allow drag and drop to start with any mouse click that is propogated
14793      * by the browser
14794      * @property primaryButtonOnly
14795      * @type boolean
14796      */
14797     primaryButtonOnly: true,
14798
14799     /**
14800      * The availabe property is false until the linked dom element is accessible.
14801      * @property available
14802      * @type boolean
14803      */
14804     available: false,
14805
14806     /**
14807      * By default, drags can only be initiated if the mousedown occurs in the
14808      * region the linked element is.  This is done in part to work around a
14809      * bug in some browsers that mis-report the mousedown if the previous
14810      * mouseup happened outside of the window.  This property is set to true
14811      * if outer handles are defined.
14812      *
14813      * @property hasOuterHandles
14814      * @type boolean
14815      * @default false
14816      */
14817     hasOuterHandles: false,
14818
14819     /**
14820      * Code that executes immediately before the startDrag event
14821      * @method b4StartDrag
14822      * @private
14823      */
14824     b4StartDrag: function(x, y) { },
14825
14826     /**
14827      * Abstract method called after a drag/drop object is clicked
14828      * and the drag or mousedown time thresholds have beeen met.
14829      * @method startDrag
14830      * @param {int} X click location
14831      * @param {int} Y click location
14832      */
14833     startDrag: function(x, y) { /* override this */ },
14834
14835     /**
14836      * Code that executes immediately before the onDrag event
14837      * @method b4Drag
14838      * @private
14839      */
14840     b4Drag: function(e) { },
14841
14842     /**
14843      * Abstract method called during the onMouseMove event while dragging an
14844      * object.
14845      * @method onDrag
14846      * @param {Event} e the mousemove event
14847      */
14848     onDrag: function(e) { /* override this */ },
14849
14850     /**
14851      * Abstract method called when this element fist begins hovering over
14852      * another DragDrop obj
14853      * @method onDragEnter
14854      * @param {Event} e the mousemove event
14855      * @param {String|DragDrop[]} id In POINT mode, the element
14856      * id this is hovering over.  In INTERSECT mode, an array of one or more
14857      * dragdrop items being hovered over.
14858      */
14859     onDragEnter: function(e, id) { /* override this */ },
14860
14861     /**
14862      * Code that executes immediately before the onDragOver event
14863      * @method b4DragOver
14864      * @private
14865      */
14866     b4DragOver: function(e) { },
14867
14868     /**
14869      * Abstract method called when this element is hovering over another
14870      * DragDrop obj
14871      * @method onDragOver
14872      * @param {Event} e the mousemove event
14873      * @param {String|DragDrop[]} id In POINT mode, the element
14874      * id this is hovering over.  In INTERSECT mode, an array of dd items
14875      * being hovered over.
14876      */
14877     onDragOver: function(e, id) { /* override this */ },
14878
14879     /**
14880      * Code that executes immediately before the onDragOut event
14881      * @method b4DragOut
14882      * @private
14883      */
14884     b4DragOut: function(e) { },
14885
14886     /**
14887      * Abstract method called when we are no longer hovering over an element
14888      * @method onDragOut
14889      * @param {Event} e the mousemove event
14890      * @param {String|DragDrop[]} id In POINT mode, the element
14891      * id this was hovering over.  In INTERSECT mode, an array of dd items
14892      * that the mouse is no longer over.
14893      */
14894     onDragOut: function(e, id) { /* override this */ },
14895
14896     /**
14897      * Code that executes immediately before the onDragDrop event
14898      * @method b4DragDrop
14899      * @private
14900      */
14901     b4DragDrop: function(e) { },
14902
14903     /**
14904      * Abstract method called when this item is dropped on another DragDrop
14905      * obj
14906      * @method onDragDrop
14907      * @param {Event} e the mouseup event
14908      * @param {String|DragDrop[]} id In POINT mode, the element
14909      * id this was dropped on.  In INTERSECT mode, an array of dd items this
14910      * was dropped on.
14911      */
14912     onDragDrop: function(e, id) { /* override this */ },
14913
14914     /**
14915      * Abstract method called when this item is dropped on an area with no
14916      * drop target
14917      * @method onInvalidDrop
14918      * @param {Event} e the mouseup event
14919      */
14920     onInvalidDrop: function(e) { /* override this */ },
14921
14922     /**
14923      * Code that executes immediately before the endDrag event
14924      * @method b4EndDrag
14925      * @private
14926      */
14927     b4EndDrag: function(e) { },
14928
14929     /**
14930      * Fired when we are done dragging the object
14931      * @method endDrag
14932      * @param {Event} e the mouseup event
14933      */
14934     endDrag: function(e) { /* override this */ },
14935
14936     /**
14937      * Code executed immediately before the onMouseDown event
14938      * @method b4MouseDown
14939      * @param {Event} e the mousedown event
14940      * @private
14941      */
14942     b4MouseDown: function(e) {  },
14943
14944     /**
14945      * Event handler that fires when a drag/drop obj gets a mousedown
14946      * @method onMouseDown
14947      * @param {Event} e the mousedown event
14948      */
14949     onMouseDown: function(e) { /* override this */ },
14950
14951     /**
14952      * Event handler that fires when a drag/drop obj gets a mouseup
14953      * @method onMouseUp
14954      * @param {Event} e the mouseup event
14955      */
14956     onMouseUp: function(e) { /* override this */ },
14957
14958     /**
14959      * Override the onAvailable method to do what is needed after the initial
14960      * position was determined.
14961      * @method onAvailable
14962      */
14963     onAvailable: function () {
14964     },
14965
14966     /*
14967      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
14968      * @type Object
14969      */
14970     defaultPadding : {left:0, right:0, top:0, bottom:0},
14971
14972     /*
14973      * Initializes the drag drop object's constraints to restrict movement to a certain element.
14974  *
14975  * Usage:
14976  <pre><code>
14977  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
14978                 { dragElId: "existingProxyDiv" });
14979  dd.startDrag = function(){
14980      this.constrainTo("parent-id");
14981  };
14982  </code></pre>
14983  * Or you can initalize it using the {@link Roo.Element} object:
14984  <pre><code>
14985  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
14986      startDrag : function(){
14987          this.constrainTo("parent-id");
14988      }
14989  });
14990  </code></pre>
14991      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
14992      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
14993      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
14994      * an object containing the sides to pad. For example: {right:10, bottom:10}
14995      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
14996      */
14997     constrainTo : function(constrainTo, pad, inContent){
14998         if(typeof pad == "number"){
14999             pad = {left: pad, right:pad, top:pad, bottom:pad};
15000         }
15001         pad = pad || this.defaultPadding;
15002         var b = Roo.get(this.getEl()).getBox();
15003         var ce = Roo.get(constrainTo);
15004         var s = ce.getScroll();
15005         var c, cd = ce.dom;
15006         if(cd == document.body){
15007             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
15008         }else{
15009             xy = ce.getXY();
15010             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
15011         }
15012
15013
15014         var topSpace = b.y - c.y;
15015         var leftSpace = b.x - c.x;
15016
15017         this.resetConstraints();
15018         this.setXConstraint(leftSpace - (pad.left||0), // left
15019                 c.width - leftSpace - b.width - (pad.right||0) //right
15020         );
15021         this.setYConstraint(topSpace - (pad.top||0), //top
15022                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
15023         );
15024     },
15025
15026     /**
15027      * Returns a reference to the linked element
15028      * @method getEl
15029      * @return {HTMLElement} the html element
15030      */
15031     getEl: function() {
15032         if (!this._domRef) {
15033             this._domRef = Roo.getDom(this.id);
15034         }
15035
15036         return this._domRef;
15037     },
15038
15039     /**
15040      * Returns a reference to the actual element to drag.  By default this is
15041      * the same as the html element, but it can be assigned to another
15042      * element. An example of this can be found in Roo.dd.DDProxy
15043      * @method getDragEl
15044      * @return {HTMLElement} the html element
15045      */
15046     getDragEl: function() {
15047         return Roo.getDom(this.dragElId);
15048     },
15049
15050     /**
15051      * Sets up the DragDrop object.  Must be called in the constructor of any
15052      * Roo.dd.DragDrop subclass
15053      * @method init
15054      * @param id the id of the linked element
15055      * @param {String} sGroup the group of related items
15056      * @param {object} config configuration attributes
15057      */
15058     init: function(id, sGroup, config) {
15059         this.initTarget(id, sGroup, config);
15060         Event.on(this.id, "mousedown", this.handleMouseDown, this);
15061         // Event.on(this.id, "selectstart", Event.preventDefault);
15062     },
15063
15064     /**
15065      * Initializes Targeting functionality only... the object does not
15066      * get a mousedown handler.
15067      * @method initTarget
15068      * @param id the id of the linked element
15069      * @param {String} sGroup the group of related items
15070      * @param {object} config configuration attributes
15071      */
15072     initTarget: function(id, sGroup, config) {
15073
15074         // configuration attributes
15075         this.config = config || {};
15076
15077         // create a local reference to the drag and drop manager
15078         this.DDM = Roo.dd.DDM;
15079         // initialize the groups array
15080         this.groups = {};
15081
15082         // assume that we have an element reference instead of an id if the
15083         // parameter is not a string
15084         if (typeof id !== "string") {
15085             id = Roo.id(id);
15086         }
15087
15088         // set the id
15089         this.id = id;
15090
15091         // add to an interaction group
15092         this.addToGroup((sGroup) ? sGroup : "default");
15093
15094         // We don't want to register this as the handle with the manager
15095         // so we just set the id rather than calling the setter.
15096         this.handleElId = id;
15097
15098         // the linked element is the element that gets dragged by default
15099         this.setDragElId(id);
15100
15101         // by default, clicked anchors will not start drag operations.
15102         this.invalidHandleTypes = { A: "A" };
15103         this.invalidHandleIds = {};
15104         this.invalidHandleClasses = [];
15105
15106         this.applyConfig();
15107
15108         this.handleOnAvailable();
15109     },
15110
15111     /**
15112      * Applies the configuration parameters that were passed into the constructor.
15113      * This is supposed to happen at each level through the inheritance chain.  So
15114      * a DDProxy implentation will execute apply config on DDProxy, DD, and
15115      * DragDrop in order to get all of the parameters that are available in
15116      * each object.
15117      * @method applyConfig
15118      */
15119     applyConfig: function() {
15120
15121         // configurable properties:
15122         //    padding, isTarget, maintainOffset, primaryButtonOnly
15123         this.padding           = this.config.padding || [0, 0, 0, 0];
15124         this.isTarget          = (this.config.isTarget !== false);
15125         this.maintainOffset    = (this.config.maintainOffset);
15126         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
15127
15128     },
15129
15130     /**
15131      * Executed when the linked element is available
15132      * @method handleOnAvailable
15133      * @private
15134      */
15135     handleOnAvailable: function() {
15136         this.available = true;
15137         this.resetConstraints();
15138         this.onAvailable();
15139     },
15140
15141      /**
15142      * Configures the padding for the target zone in px.  Effectively expands
15143      * (or reduces) the virtual object size for targeting calculations.
15144      * Supports css-style shorthand; if only one parameter is passed, all sides
15145      * will have that padding, and if only two are passed, the top and bottom
15146      * will have the first param, the left and right the second.
15147      * @method setPadding
15148      * @param {int} iTop    Top pad
15149      * @param {int} iRight  Right pad
15150      * @param {int} iBot    Bot pad
15151      * @param {int} iLeft   Left pad
15152      */
15153     setPadding: function(iTop, iRight, iBot, iLeft) {
15154         // this.padding = [iLeft, iRight, iTop, iBot];
15155         if (!iRight && 0 !== iRight) {
15156             this.padding = [iTop, iTop, iTop, iTop];
15157         } else if (!iBot && 0 !== iBot) {
15158             this.padding = [iTop, iRight, iTop, iRight];
15159         } else {
15160             this.padding = [iTop, iRight, iBot, iLeft];
15161         }
15162     },
15163
15164     /**
15165      * Stores the initial placement of the linked element.
15166      * @method setInitialPosition
15167      * @param {int} diffX   the X offset, default 0
15168      * @param {int} diffY   the Y offset, default 0
15169      */
15170     setInitPosition: function(diffX, diffY) {
15171         var el = this.getEl();
15172
15173         if (!this.DDM.verifyEl(el)) {
15174             return;
15175         }
15176
15177         var dx = diffX || 0;
15178         var dy = diffY || 0;
15179
15180         var p = Dom.getXY( el );
15181
15182         this.initPageX = p[0] - dx;
15183         this.initPageY = p[1] - dy;
15184
15185         this.lastPageX = p[0];
15186         this.lastPageY = p[1];
15187
15188
15189         this.setStartPosition(p);
15190     },
15191
15192     /**
15193      * Sets the start position of the element.  This is set when the obj
15194      * is initialized, the reset when a drag is started.
15195      * @method setStartPosition
15196      * @param pos current position (from previous lookup)
15197      * @private
15198      */
15199     setStartPosition: function(pos) {
15200         var p = pos || Dom.getXY( this.getEl() );
15201         this.deltaSetXY = null;
15202
15203         this.startPageX = p[0];
15204         this.startPageY = p[1];
15205     },
15206
15207     /**
15208      * Add this instance to a group of related drag/drop objects.  All
15209      * instances belong to at least one group, and can belong to as many
15210      * groups as needed.
15211      * @method addToGroup
15212      * @param sGroup {string} the name of the group
15213      */
15214     addToGroup: function(sGroup) {
15215         this.groups[sGroup] = true;
15216         this.DDM.regDragDrop(this, sGroup);
15217     },
15218
15219     /**
15220      * Remove's this instance from the supplied interaction group
15221      * @method removeFromGroup
15222      * @param {string}  sGroup  The group to drop
15223      */
15224     removeFromGroup: function(sGroup) {
15225         if (this.groups[sGroup]) {
15226             delete this.groups[sGroup];
15227         }
15228
15229         this.DDM.removeDDFromGroup(this, sGroup);
15230     },
15231
15232     /**
15233      * Allows you to specify that an element other than the linked element
15234      * will be moved with the cursor during a drag
15235      * @method setDragElId
15236      * @param id {string} the id of the element that will be used to initiate the drag
15237      */
15238     setDragElId: function(id) {
15239         this.dragElId = id;
15240     },
15241
15242     /**
15243      * Allows you to specify a child of the linked element that should be
15244      * used to initiate the drag operation.  An example of this would be if
15245      * you have a content div with text and links.  Clicking anywhere in the
15246      * content area would normally start the drag operation.  Use this method
15247      * to specify that an element inside of the content div is the element
15248      * that starts the drag operation.
15249      * @method setHandleElId
15250      * @param id {string} the id of the element that will be used to
15251      * initiate the drag.
15252      */
15253     setHandleElId: function(id) {
15254         if (typeof id !== "string") {
15255             id = Roo.id(id);
15256         }
15257         this.handleElId = id;
15258         this.DDM.regHandle(this.id, id);
15259     },
15260
15261     /**
15262      * Allows you to set an element outside of the linked element as a drag
15263      * handle
15264      * @method setOuterHandleElId
15265      * @param id the id of the element that will be used to initiate the drag
15266      */
15267     setOuterHandleElId: function(id) {
15268         if (typeof id !== "string") {
15269             id = Roo.id(id);
15270         }
15271         Event.on(id, "mousedown",
15272                 this.handleMouseDown, this);
15273         this.setHandleElId(id);
15274
15275         this.hasOuterHandles = true;
15276     },
15277
15278     /**
15279      * Remove all drag and drop hooks for this element
15280      * @method unreg
15281      */
15282     unreg: function() {
15283         Event.un(this.id, "mousedown",
15284                 this.handleMouseDown);
15285         this._domRef = null;
15286         this.DDM._remove(this);
15287     },
15288
15289     destroy : function(){
15290         this.unreg();
15291     },
15292
15293     /**
15294      * Returns true if this instance is locked, or the drag drop mgr is locked
15295      * (meaning that all drag/drop is disabled on the page.)
15296      * @method isLocked
15297      * @return {boolean} true if this obj or all drag/drop is locked, else
15298      * false
15299      */
15300     isLocked: function() {
15301         return (this.DDM.isLocked() || this.locked);
15302     },
15303
15304     /**
15305      * Fired when this object is clicked
15306      * @method handleMouseDown
15307      * @param {Event} e
15308      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
15309      * @private
15310      */
15311     handleMouseDown: function(e, oDD){
15312         if (this.primaryButtonOnly && e.button != 0) {
15313             return;
15314         }
15315
15316         if (this.isLocked()) {
15317             return;
15318         }
15319
15320         this.DDM.refreshCache(this.groups);
15321
15322         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
15323         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
15324         } else {
15325             if (this.clickValidator(e)) {
15326
15327                 // set the initial element position
15328                 this.setStartPosition();
15329
15330
15331                 this.b4MouseDown(e);
15332                 this.onMouseDown(e);
15333
15334                 this.DDM.handleMouseDown(e, this);
15335
15336                 this.DDM.stopEvent(e);
15337             } else {
15338
15339
15340             }
15341         }
15342     },
15343
15344     clickValidator: function(e) {
15345         var target = e.getTarget();
15346         return ( this.isValidHandleChild(target) &&
15347                     (this.id == this.handleElId ||
15348                         this.DDM.handleWasClicked(target, this.id)) );
15349     },
15350
15351     /**
15352      * Allows you to specify a tag name that should not start a drag operation
15353      * when clicked.  This is designed to facilitate embedding links within a
15354      * drag handle that do something other than start the drag.
15355      * @method addInvalidHandleType
15356      * @param {string} tagName the type of element to exclude
15357      */
15358     addInvalidHandleType: function(tagName) {
15359         var type = tagName.toUpperCase();
15360         this.invalidHandleTypes[type] = type;
15361     },
15362
15363     /**
15364      * Lets you to specify an element id for a child of a drag handle
15365      * that should not initiate a drag
15366      * @method addInvalidHandleId
15367      * @param {string} id the element id of the element you wish to ignore
15368      */
15369     addInvalidHandleId: function(id) {
15370         if (typeof id !== "string") {
15371             id = Roo.id(id);
15372         }
15373         this.invalidHandleIds[id] = id;
15374     },
15375
15376     /**
15377      * Lets you specify a css class of elements that will not initiate a drag
15378      * @method addInvalidHandleClass
15379      * @param {string} cssClass the class of the elements you wish to ignore
15380      */
15381     addInvalidHandleClass: function(cssClass) {
15382         this.invalidHandleClasses.push(cssClass);
15383     },
15384
15385     /**
15386      * Unsets an excluded tag name set by addInvalidHandleType
15387      * @method removeInvalidHandleType
15388      * @param {string} tagName the type of element to unexclude
15389      */
15390     removeInvalidHandleType: function(tagName) {
15391         var type = tagName.toUpperCase();
15392         // this.invalidHandleTypes[type] = null;
15393         delete this.invalidHandleTypes[type];
15394     },
15395
15396     /**
15397      * Unsets an invalid handle id
15398      * @method removeInvalidHandleId
15399      * @param {string} id the id of the element to re-enable
15400      */
15401     removeInvalidHandleId: function(id) {
15402         if (typeof id !== "string") {
15403             id = Roo.id(id);
15404         }
15405         delete this.invalidHandleIds[id];
15406     },
15407
15408     /**
15409      * Unsets an invalid css class
15410      * @method removeInvalidHandleClass
15411      * @param {string} cssClass the class of the element(s) you wish to
15412      * re-enable
15413      */
15414     removeInvalidHandleClass: function(cssClass) {
15415         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
15416             if (this.invalidHandleClasses[i] == cssClass) {
15417                 delete this.invalidHandleClasses[i];
15418             }
15419         }
15420     },
15421
15422     /**
15423      * Checks the tag exclusion list to see if this click should be ignored
15424      * @method isValidHandleChild
15425      * @param {HTMLElement} node the HTMLElement to evaluate
15426      * @return {boolean} true if this is a valid tag type, false if not
15427      */
15428     isValidHandleChild: function(node) {
15429
15430         var valid = true;
15431         // var n = (node.nodeName == "#text") ? node.parentNode : node;
15432         var nodeName;
15433         try {
15434             nodeName = node.nodeName.toUpperCase();
15435         } catch(e) {
15436             nodeName = node.nodeName;
15437         }
15438         valid = valid && !this.invalidHandleTypes[nodeName];
15439         valid = valid && !this.invalidHandleIds[node.id];
15440
15441         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
15442             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
15443         }
15444
15445
15446         return valid;
15447
15448     },
15449
15450     /**
15451      * Create the array of horizontal tick marks if an interval was specified
15452      * in setXConstraint().
15453      * @method setXTicks
15454      * @private
15455      */
15456     setXTicks: function(iStartX, iTickSize) {
15457         this.xTicks = [];
15458         this.xTickSize = iTickSize;
15459
15460         var tickMap = {};
15461
15462         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
15463             if (!tickMap[i]) {
15464                 this.xTicks[this.xTicks.length] = i;
15465                 tickMap[i] = true;
15466             }
15467         }
15468
15469         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
15470             if (!tickMap[i]) {
15471                 this.xTicks[this.xTicks.length] = i;
15472                 tickMap[i] = true;
15473             }
15474         }
15475
15476         this.xTicks.sort(this.DDM.numericSort) ;
15477     },
15478
15479     /**
15480      * Create the array of vertical tick marks if an interval was specified in
15481      * setYConstraint().
15482      * @method setYTicks
15483      * @private
15484      */
15485     setYTicks: function(iStartY, iTickSize) {
15486         this.yTicks = [];
15487         this.yTickSize = iTickSize;
15488
15489         var tickMap = {};
15490
15491         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
15492             if (!tickMap[i]) {
15493                 this.yTicks[this.yTicks.length] = i;
15494                 tickMap[i] = true;
15495             }
15496         }
15497
15498         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
15499             if (!tickMap[i]) {
15500                 this.yTicks[this.yTicks.length] = i;
15501                 tickMap[i] = true;
15502             }
15503         }
15504
15505         this.yTicks.sort(this.DDM.numericSort) ;
15506     },
15507
15508     /**
15509      * By default, the element can be dragged any place on the screen.  Use
15510      * this method to limit the horizontal travel of the element.  Pass in
15511      * 0,0 for the parameters if you want to lock the drag to the y axis.
15512      * @method setXConstraint
15513      * @param {int} iLeft the number of pixels the element can move to the left
15514      * @param {int} iRight the number of pixels the element can move to the
15515      * right
15516      * @param {int} iTickSize optional parameter for specifying that the
15517      * element
15518      * should move iTickSize pixels at a time.
15519      */
15520     setXConstraint: function(iLeft, iRight, iTickSize) {
15521         this.leftConstraint = iLeft;
15522         this.rightConstraint = iRight;
15523
15524         this.minX = this.initPageX - iLeft;
15525         this.maxX = this.initPageX + iRight;
15526         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
15527
15528         this.constrainX = true;
15529     },
15530
15531     /**
15532      * Clears any constraints applied to this instance.  Also clears ticks
15533      * since they can't exist independent of a constraint at this time.
15534      * @method clearConstraints
15535      */
15536     clearConstraints: function() {
15537         this.constrainX = false;
15538         this.constrainY = false;
15539         this.clearTicks();
15540     },
15541
15542     /**
15543      * Clears any tick interval defined for this instance
15544      * @method clearTicks
15545      */
15546     clearTicks: function() {
15547         this.xTicks = null;
15548         this.yTicks = null;
15549         this.xTickSize = 0;
15550         this.yTickSize = 0;
15551     },
15552
15553     /**
15554      * By default, the element can be dragged any place on the screen.  Set
15555      * this to limit the vertical travel of the element.  Pass in 0,0 for the
15556      * parameters if you want to lock the drag to the x axis.
15557      * @method setYConstraint
15558      * @param {int} iUp the number of pixels the element can move up
15559      * @param {int} iDown the number of pixels the element can move down
15560      * @param {int} iTickSize optional parameter for specifying that the
15561      * element should move iTickSize pixels at a time.
15562      */
15563     setYConstraint: function(iUp, iDown, iTickSize) {
15564         this.topConstraint = iUp;
15565         this.bottomConstraint = iDown;
15566
15567         this.minY = this.initPageY - iUp;
15568         this.maxY = this.initPageY + iDown;
15569         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
15570
15571         this.constrainY = true;
15572
15573     },
15574
15575     /**
15576      * resetConstraints must be called if you manually reposition a dd element.
15577      * @method resetConstraints
15578      * @param {boolean} maintainOffset
15579      */
15580     resetConstraints: function() {
15581
15582
15583         // Maintain offsets if necessary
15584         if (this.initPageX || this.initPageX === 0) {
15585             // figure out how much this thing has moved
15586             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
15587             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
15588
15589             this.setInitPosition(dx, dy);
15590
15591         // This is the first time we have detected the element's position
15592         } else {
15593             this.setInitPosition();
15594         }
15595
15596         if (this.constrainX) {
15597             this.setXConstraint( this.leftConstraint,
15598                                  this.rightConstraint,
15599                                  this.xTickSize        );
15600         }
15601
15602         if (this.constrainY) {
15603             this.setYConstraint( this.topConstraint,
15604                                  this.bottomConstraint,
15605                                  this.yTickSize         );
15606         }
15607     },
15608
15609     /**
15610      * Normally the drag element is moved pixel by pixel, but we can specify
15611      * that it move a number of pixels at a time.  This method resolves the
15612      * location when we have it set up like this.
15613      * @method getTick
15614      * @param {int} val where we want to place the object
15615      * @param {int[]} tickArray sorted array of valid points
15616      * @return {int} the closest tick
15617      * @private
15618      */
15619     getTick: function(val, tickArray) {
15620
15621         if (!tickArray) {
15622             // If tick interval is not defined, it is effectively 1 pixel,
15623             // so we return the value passed to us.
15624             return val;
15625         } else if (tickArray[0] >= val) {
15626             // The value is lower than the first tick, so we return the first
15627             // tick.
15628             return tickArray[0];
15629         } else {
15630             for (var i=0, len=tickArray.length; i<len; ++i) {
15631                 var next = i + 1;
15632                 if (tickArray[next] && tickArray[next] >= val) {
15633                     var diff1 = val - tickArray[i];
15634                     var diff2 = tickArray[next] - val;
15635                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
15636                 }
15637             }
15638
15639             // The value is larger than the last tick, so we return the last
15640             // tick.
15641             return tickArray[tickArray.length - 1];
15642         }
15643     },
15644
15645     /**
15646      * toString method
15647      * @method toString
15648      * @return {string} string representation of the dd obj
15649      */
15650     toString: function() {
15651         return ("DragDrop " + this.id);
15652     }
15653
15654 });
15655
15656 })();
15657 /*
15658  * Based on:
15659  * Ext JS Library 1.1.1
15660  * Copyright(c) 2006-2007, Ext JS, LLC.
15661  *
15662  * Originally Released Under LGPL - original licence link has changed is not relivant.
15663  *
15664  * Fork - LGPL
15665  * <script type="text/javascript">
15666  */
15667
15668
15669 /**
15670  * The drag and drop utility provides a framework for building drag and drop
15671  * applications.  In addition to enabling drag and drop for specific elements,
15672  * the drag and drop elements are tracked by the manager class, and the
15673  * interactions between the various elements are tracked during the drag and
15674  * the implementing code is notified about these important moments.
15675  */
15676
15677 // Only load the library once.  Rewriting the manager class would orphan
15678 // existing drag and drop instances.
15679 if (!Roo.dd.DragDropMgr) {
15680
15681 /**
15682  * @class Roo.dd.DragDropMgr
15683  * DragDropMgr is a singleton that tracks the element interaction for
15684  * all DragDrop items in the window.  Generally, you will not call
15685  * this class directly, but it does have helper methods that could
15686  * be useful in your DragDrop implementations.
15687  * @singleton
15688  */
15689 Roo.dd.DragDropMgr = function() {
15690
15691     var Event = Roo.EventManager;
15692
15693     return {
15694
15695         /**
15696          * Two dimensional Array of registered DragDrop objects.  The first
15697          * dimension is the DragDrop item group, the second the DragDrop
15698          * object.
15699          * @property ids
15700          * @type {string: string}
15701          * @private
15702          * @static
15703          */
15704         ids: {},
15705
15706         /**
15707          * Array of element ids defined as drag handles.  Used to determine
15708          * if the element that generated the mousedown event is actually the
15709          * handle and not the html element itself.
15710          * @property handleIds
15711          * @type {string: string}
15712          * @private
15713          * @static
15714          */
15715         handleIds: {},
15716
15717         /**
15718          * the DragDrop object that is currently being dragged
15719          * @property dragCurrent
15720          * @type DragDrop
15721          * @private
15722          * @static
15723          **/
15724         dragCurrent: null,
15725
15726         /**
15727          * the DragDrop object(s) that are being hovered over
15728          * @property dragOvers
15729          * @type Array
15730          * @private
15731          * @static
15732          */
15733         dragOvers: {},
15734
15735         /**
15736          * the X distance between the cursor and the object being dragged
15737          * @property deltaX
15738          * @type int
15739          * @private
15740          * @static
15741          */
15742         deltaX: 0,
15743
15744         /**
15745          * the Y distance between the cursor and the object being dragged
15746          * @property deltaY
15747          * @type int
15748          * @private
15749          * @static
15750          */
15751         deltaY: 0,
15752
15753         /**
15754          * Flag to determine if we should prevent the default behavior of the
15755          * events we define. By default this is true, but this can be set to
15756          * false if you need the default behavior (not recommended)
15757          * @property preventDefault
15758          * @type boolean
15759          * @static
15760          */
15761         preventDefault: true,
15762
15763         /**
15764          * Flag to determine if we should stop the propagation of the events
15765          * we generate. This is true by default but you may want to set it to
15766          * false if the html element contains other features that require the
15767          * mouse click.
15768          * @property stopPropagation
15769          * @type boolean
15770          * @static
15771          */
15772         stopPropagation: true,
15773
15774         /**
15775          * Internal flag that is set to true when drag and drop has been
15776          * intialized
15777          * @property initialized
15778          * @private
15779          * @static
15780          */
15781         initalized: false,
15782
15783         /**
15784          * All drag and drop can be disabled.
15785          * @property locked
15786          * @private
15787          * @static
15788          */
15789         locked: false,
15790
15791         /**
15792          * Called the first time an element is registered.
15793          * @method init
15794          * @private
15795          * @static
15796          */
15797         init: function() {
15798             this.initialized = true;
15799         },
15800
15801         /**
15802          * In point mode, drag and drop interaction is defined by the
15803          * location of the cursor during the drag/drop
15804          * @property POINT
15805          * @type int
15806          * @static
15807          */
15808         POINT: 0,
15809
15810         /**
15811          * In intersect mode, drag and drop interactio nis defined by the
15812          * overlap of two or more drag and drop objects.
15813          * @property INTERSECT
15814          * @type int
15815          * @static
15816          */
15817         INTERSECT: 1,
15818
15819         /**
15820          * The current drag and drop mode.  Default: POINT
15821          * @property mode
15822          * @type int
15823          * @static
15824          */
15825         mode: 0,
15826
15827         /**
15828          * Runs method on all drag and drop objects
15829          * @method _execOnAll
15830          * @private
15831          * @static
15832          */
15833         _execOnAll: function(sMethod, args) {
15834             for (var i in this.ids) {
15835                 for (var j in this.ids[i]) {
15836                     var oDD = this.ids[i][j];
15837                     if (! this.isTypeOfDD(oDD)) {
15838                         continue;
15839                     }
15840                     oDD[sMethod].apply(oDD, args);
15841                 }
15842             }
15843         },
15844
15845         /**
15846          * Drag and drop initialization.  Sets up the global event handlers
15847          * @method _onLoad
15848          * @private
15849          * @static
15850          */
15851         _onLoad: function() {
15852
15853             this.init();
15854
15855
15856             Event.on(document, "mouseup",   this.handleMouseUp, this, true);
15857             Event.on(document, "mousemove", this.handleMouseMove, this, true);
15858             Event.on(window,   "unload",    this._onUnload, this, true);
15859             Event.on(window,   "resize",    this._onResize, this, true);
15860             // Event.on(window,   "mouseout",    this._test);
15861
15862         },
15863
15864         /**
15865          * Reset constraints on all drag and drop objs
15866          * @method _onResize
15867          * @private
15868          * @static
15869          */
15870         _onResize: function(e) {
15871             this._execOnAll("resetConstraints", []);
15872         },
15873
15874         /**
15875          * Lock all drag and drop functionality
15876          * @method lock
15877          * @static
15878          */
15879         lock: function() { this.locked = true; },
15880
15881         /**
15882          * Unlock all drag and drop functionality
15883          * @method unlock
15884          * @static
15885          */
15886         unlock: function() { this.locked = false; },
15887
15888         /**
15889          * Is drag and drop locked?
15890          * @method isLocked
15891          * @return {boolean} True if drag and drop is locked, false otherwise.
15892          * @static
15893          */
15894         isLocked: function() { return this.locked; },
15895
15896         /**
15897          * Location cache that is set for all drag drop objects when a drag is
15898          * initiated, cleared when the drag is finished.
15899          * @property locationCache
15900          * @private
15901          * @static
15902          */
15903         locationCache: {},
15904
15905         /**
15906          * Set useCache to false if you want to force object the lookup of each
15907          * drag and drop linked element constantly during a drag.
15908          * @property useCache
15909          * @type boolean
15910          * @static
15911          */
15912         useCache: true,
15913
15914         /**
15915          * The number of pixels that the mouse needs to move after the
15916          * mousedown before the drag is initiated.  Default=3;
15917          * @property clickPixelThresh
15918          * @type int
15919          * @static
15920          */
15921         clickPixelThresh: 3,
15922
15923         /**
15924          * The number of milliseconds after the mousedown event to initiate the
15925          * drag if we don't get a mouseup event. Default=1000
15926          * @property clickTimeThresh
15927          * @type int
15928          * @static
15929          */
15930         clickTimeThresh: 350,
15931
15932         /**
15933          * Flag that indicates that either the drag pixel threshold or the
15934          * mousdown time threshold has been met
15935          * @property dragThreshMet
15936          * @type boolean
15937          * @private
15938          * @static
15939          */
15940         dragThreshMet: false,
15941
15942         /**
15943          * Timeout used for the click time threshold
15944          * @property clickTimeout
15945          * @type Object
15946          * @private
15947          * @static
15948          */
15949         clickTimeout: null,
15950
15951         /**
15952          * The X position of the mousedown event stored for later use when a
15953          * drag threshold is met.
15954          * @property startX
15955          * @type int
15956          * @private
15957          * @static
15958          */
15959         startX: 0,
15960
15961         /**
15962          * The Y position of the mousedown event stored for later use when a
15963          * drag threshold is met.
15964          * @property startY
15965          * @type int
15966          * @private
15967          * @static
15968          */
15969         startY: 0,
15970
15971         /**
15972          * Each DragDrop instance must be registered with the DragDropMgr.
15973          * This is executed in DragDrop.init()
15974          * @method regDragDrop
15975          * @param {DragDrop} oDD the DragDrop object to register
15976          * @param {String} sGroup the name of the group this element belongs to
15977          * @static
15978          */
15979         regDragDrop: function(oDD, sGroup) {
15980             if (!this.initialized) { this.init(); }
15981
15982             if (!this.ids[sGroup]) {
15983                 this.ids[sGroup] = {};
15984             }
15985             this.ids[sGroup][oDD.id] = oDD;
15986         },
15987
15988         /**
15989          * Removes the supplied dd instance from the supplied group. Executed
15990          * by DragDrop.removeFromGroup, so don't call this function directly.
15991          * @method removeDDFromGroup
15992          * @private
15993          * @static
15994          */
15995         removeDDFromGroup: function(oDD, sGroup) {
15996             if (!this.ids[sGroup]) {
15997                 this.ids[sGroup] = {};
15998             }
15999
16000             var obj = this.ids[sGroup];
16001             if (obj && obj[oDD.id]) {
16002                 delete obj[oDD.id];
16003             }
16004         },
16005
16006         /**
16007          * Unregisters a drag and drop item.  This is executed in
16008          * DragDrop.unreg, use that method instead of calling this directly.
16009          * @method _remove
16010          * @private
16011          * @static
16012          */
16013         _remove: function(oDD) {
16014             for (var g in oDD.groups) {
16015                 if (g && this.ids[g][oDD.id]) {
16016                     delete this.ids[g][oDD.id];
16017                 }
16018             }
16019             delete this.handleIds[oDD.id];
16020         },
16021
16022         /**
16023          * Each DragDrop handle element must be registered.  This is done
16024          * automatically when executing DragDrop.setHandleElId()
16025          * @method regHandle
16026          * @param {String} sDDId the DragDrop id this element is a handle for
16027          * @param {String} sHandleId the id of the element that is the drag
16028          * handle
16029          * @static
16030          */
16031         regHandle: function(sDDId, sHandleId) {
16032             if (!this.handleIds[sDDId]) {
16033                 this.handleIds[sDDId] = {};
16034             }
16035             this.handleIds[sDDId][sHandleId] = sHandleId;
16036         },
16037
16038         /**
16039          * Utility function to determine if a given element has been
16040          * registered as a drag drop item.
16041          * @method isDragDrop
16042          * @param {String} id the element id to check
16043          * @return {boolean} true if this element is a DragDrop item,
16044          * false otherwise
16045          * @static
16046          */
16047         isDragDrop: function(id) {
16048             return ( this.getDDById(id) ) ? true : false;
16049         },
16050
16051         /**
16052          * Returns the drag and drop instances that are in all groups the
16053          * passed in instance belongs to.
16054          * @method getRelated
16055          * @param {DragDrop} p_oDD the obj to get related data for
16056          * @param {boolean} bTargetsOnly if true, only return targetable objs
16057          * @return {DragDrop[]} the related instances
16058          * @static
16059          */
16060         getRelated: function(p_oDD, bTargetsOnly) {
16061             var oDDs = [];
16062             for (var i in p_oDD.groups) {
16063                 for (j in this.ids[i]) {
16064                     var dd = this.ids[i][j];
16065                     if (! this.isTypeOfDD(dd)) {
16066                         continue;
16067                     }
16068                     if (!bTargetsOnly || dd.isTarget) {
16069                         oDDs[oDDs.length] = dd;
16070                     }
16071                 }
16072             }
16073
16074             return oDDs;
16075         },
16076
16077         /**
16078          * Returns true if the specified dd target is a legal target for
16079          * the specifice drag obj
16080          * @method isLegalTarget
16081          * @param {DragDrop} the drag obj
16082          * @param {DragDrop} the target
16083          * @return {boolean} true if the target is a legal target for the
16084          * dd obj
16085          * @static
16086          */
16087         isLegalTarget: function (oDD, oTargetDD) {
16088             var targets = this.getRelated(oDD, true);
16089             for (var i=0, len=targets.length;i<len;++i) {
16090                 if (targets[i].id == oTargetDD.id) {
16091                     return true;
16092                 }
16093             }
16094
16095             return false;
16096         },
16097
16098         /**
16099          * My goal is to be able to transparently determine if an object is
16100          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
16101          * returns "object", oDD.constructor.toString() always returns
16102          * "DragDrop" and not the name of the subclass.  So for now it just
16103          * evaluates a well-known variable in DragDrop.
16104          * @method isTypeOfDD
16105          * @param {Object} the object to evaluate
16106          * @return {boolean} true if typeof oDD = DragDrop
16107          * @static
16108          */
16109         isTypeOfDD: function (oDD) {
16110             return (oDD && oDD.__ygDragDrop);
16111         },
16112
16113         /**
16114          * Utility function to determine if a given element has been
16115          * registered as a drag drop handle for the given Drag Drop object.
16116          * @method isHandle
16117          * @param {String} id the element id to check
16118          * @return {boolean} true if this element is a DragDrop handle, false
16119          * otherwise
16120          * @static
16121          */
16122         isHandle: function(sDDId, sHandleId) {
16123             return ( this.handleIds[sDDId] &&
16124                             this.handleIds[sDDId][sHandleId] );
16125         },
16126
16127         /**
16128          * Returns the DragDrop instance for a given id
16129          * @method getDDById
16130          * @param {String} id the id of the DragDrop object
16131          * @return {DragDrop} the drag drop object, null if it is not found
16132          * @static
16133          */
16134         getDDById: function(id) {
16135             for (var i in this.ids) {
16136                 if (this.ids[i][id]) {
16137                     return this.ids[i][id];
16138                 }
16139             }
16140             return null;
16141         },
16142
16143         /**
16144          * Fired after a registered DragDrop object gets the mousedown event.
16145          * Sets up the events required to track the object being dragged
16146          * @method handleMouseDown
16147          * @param {Event} e the event
16148          * @param oDD the DragDrop object being dragged
16149          * @private
16150          * @static
16151          */
16152         handleMouseDown: function(e, oDD) {
16153             if(Roo.QuickTips){
16154                 Roo.QuickTips.disable();
16155             }
16156             this.currentTarget = e.getTarget();
16157
16158             this.dragCurrent = oDD;
16159
16160             var el = oDD.getEl();
16161
16162             // track start position
16163             this.startX = e.getPageX();
16164             this.startY = e.getPageY();
16165
16166             this.deltaX = this.startX - el.offsetLeft;
16167             this.deltaY = this.startY - el.offsetTop;
16168
16169             this.dragThreshMet = false;
16170
16171             this.clickTimeout = setTimeout(
16172                     function() {
16173                         var DDM = Roo.dd.DDM;
16174                         DDM.startDrag(DDM.startX, DDM.startY);
16175                     },
16176                     this.clickTimeThresh );
16177         },
16178
16179         /**
16180          * Fired when either the drag pixel threshol or the mousedown hold
16181          * time threshold has been met.
16182          * @method startDrag
16183          * @param x {int} the X position of the original mousedown
16184          * @param y {int} the Y position of the original mousedown
16185          * @static
16186          */
16187         startDrag: function(x, y) {
16188             clearTimeout(this.clickTimeout);
16189             if (this.dragCurrent) {
16190                 this.dragCurrent.b4StartDrag(x, y);
16191                 this.dragCurrent.startDrag(x, y);
16192             }
16193             this.dragThreshMet = true;
16194         },
16195
16196         /**
16197          * Internal function to handle the mouseup event.  Will be invoked
16198          * from the context of the document.
16199          * @method handleMouseUp
16200          * @param {Event} e the event
16201          * @private
16202          * @static
16203          */
16204         handleMouseUp: function(e) {
16205
16206             if(Roo.QuickTips){
16207                 Roo.QuickTips.enable();
16208             }
16209             if (! this.dragCurrent) {
16210                 return;
16211             }
16212
16213             clearTimeout(this.clickTimeout);
16214
16215             if (this.dragThreshMet) {
16216                 this.fireEvents(e, true);
16217             } else {
16218             }
16219
16220             this.stopDrag(e);
16221
16222             this.stopEvent(e);
16223         },
16224
16225         /**
16226          * Utility to stop event propagation and event default, if these
16227          * features are turned on.
16228          * @method stopEvent
16229          * @param {Event} e the event as returned by this.getEvent()
16230          * @static
16231          */
16232         stopEvent: function(e){
16233             if(this.stopPropagation) {
16234                 e.stopPropagation();
16235             }
16236
16237             if (this.preventDefault) {
16238                 e.preventDefault();
16239             }
16240         },
16241
16242         /**
16243          * Internal function to clean up event handlers after the drag
16244          * operation is complete
16245          * @method stopDrag
16246          * @param {Event} e the event
16247          * @private
16248          * @static
16249          */
16250         stopDrag: function(e) {
16251             // Fire the drag end event for the item that was dragged
16252             if (this.dragCurrent) {
16253                 if (this.dragThreshMet) {
16254                     this.dragCurrent.b4EndDrag(e);
16255                     this.dragCurrent.endDrag(e);
16256                 }
16257
16258                 this.dragCurrent.onMouseUp(e);
16259             }
16260
16261             this.dragCurrent = null;
16262             this.dragOvers = {};
16263         },
16264
16265         /**
16266          * Internal function to handle the mousemove event.  Will be invoked
16267          * from the context of the html element.
16268          *
16269          * @TODO figure out what we can do about mouse events lost when the
16270          * user drags objects beyond the window boundary.  Currently we can
16271          * detect this in internet explorer by verifying that the mouse is
16272          * down during the mousemove event.  Firefox doesn't give us the
16273          * button state on the mousemove event.
16274          * @method handleMouseMove
16275          * @param {Event} e the event
16276          * @private
16277          * @static
16278          */
16279         handleMouseMove: function(e) {
16280             if (! this.dragCurrent) {
16281                 return true;
16282             }
16283
16284             // var button = e.which || e.button;
16285
16286             // check for IE mouseup outside of page boundary
16287             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
16288                 this.stopEvent(e);
16289                 return this.handleMouseUp(e);
16290             }
16291
16292             if (!this.dragThreshMet) {
16293                 var diffX = Math.abs(this.startX - e.getPageX());
16294                 var diffY = Math.abs(this.startY - e.getPageY());
16295                 if (diffX > this.clickPixelThresh ||
16296                             diffY > this.clickPixelThresh) {
16297                     this.startDrag(this.startX, this.startY);
16298                 }
16299             }
16300
16301             if (this.dragThreshMet) {
16302                 this.dragCurrent.b4Drag(e);
16303                 this.dragCurrent.onDrag(e);
16304                 if(!this.dragCurrent.moveOnly){
16305                     this.fireEvents(e, false);
16306                 }
16307             }
16308
16309             this.stopEvent(e);
16310
16311             return true;
16312         },
16313
16314         /**
16315          * Iterates over all of the DragDrop elements to find ones we are
16316          * hovering over or dropping on
16317          * @method fireEvents
16318          * @param {Event} e the event
16319          * @param {boolean} isDrop is this a drop op or a mouseover op?
16320          * @private
16321          * @static
16322          */
16323         fireEvents: function(e, isDrop) {
16324             var dc = this.dragCurrent;
16325
16326             // If the user did the mouse up outside of the window, we could
16327             // get here even though we have ended the drag.
16328             if (!dc || dc.isLocked()) {
16329                 return;
16330             }
16331
16332             var pt = e.getPoint();
16333
16334             // cache the previous dragOver array
16335             var oldOvers = [];
16336
16337             var outEvts   = [];
16338             var overEvts  = [];
16339             var dropEvts  = [];
16340             var enterEvts = [];
16341
16342             // Check to see if the object(s) we were hovering over is no longer
16343             // being hovered over so we can fire the onDragOut event
16344             for (var i in this.dragOvers) {
16345
16346                 var ddo = this.dragOvers[i];
16347
16348                 if (! this.isTypeOfDD(ddo)) {
16349                     continue;
16350                 }
16351
16352                 if (! this.isOverTarget(pt, ddo, this.mode)) {
16353                     outEvts.push( ddo );
16354                 }
16355
16356                 oldOvers[i] = true;
16357                 delete this.dragOvers[i];
16358             }
16359
16360             for (var sGroup in dc.groups) {
16361
16362                 if ("string" != typeof sGroup) {
16363                     continue;
16364                 }
16365
16366                 for (i in this.ids[sGroup]) {
16367                     var oDD = this.ids[sGroup][i];
16368                     if (! this.isTypeOfDD(oDD)) {
16369                         continue;
16370                     }
16371
16372                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
16373                         if (this.isOverTarget(pt, oDD, this.mode)) {
16374                             // look for drop interactions
16375                             if (isDrop) {
16376                                 dropEvts.push( oDD );
16377                             // look for drag enter and drag over interactions
16378                             } else {
16379
16380                                 // initial drag over: dragEnter fires
16381                                 if (!oldOvers[oDD.id]) {
16382                                     enterEvts.push( oDD );
16383                                 // subsequent drag overs: dragOver fires
16384                                 } else {
16385                                     overEvts.push( oDD );
16386                                 }
16387
16388                                 this.dragOvers[oDD.id] = oDD;
16389                             }
16390                         }
16391                     }
16392                 }
16393             }
16394
16395             if (this.mode) {
16396                 if (outEvts.length) {
16397                     dc.b4DragOut(e, outEvts);
16398                     dc.onDragOut(e, outEvts);
16399                 }
16400
16401                 if (enterEvts.length) {
16402                     dc.onDragEnter(e, enterEvts);
16403                 }
16404
16405                 if (overEvts.length) {
16406                     dc.b4DragOver(e, overEvts);
16407                     dc.onDragOver(e, overEvts);
16408                 }
16409
16410                 if (dropEvts.length) {
16411                     dc.b4DragDrop(e, dropEvts);
16412                     dc.onDragDrop(e, dropEvts);
16413                 }
16414
16415             } else {
16416                 // fire dragout events
16417                 var len = 0;
16418                 for (i=0, len=outEvts.length; i<len; ++i) {
16419                     dc.b4DragOut(e, outEvts[i].id);
16420                     dc.onDragOut(e, outEvts[i].id);
16421                 }
16422
16423                 // fire enter events
16424                 for (i=0,len=enterEvts.length; i<len; ++i) {
16425                     // dc.b4DragEnter(e, oDD.id);
16426                     dc.onDragEnter(e, enterEvts[i].id);
16427                 }
16428
16429                 // fire over events
16430                 for (i=0,len=overEvts.length; i<len; ++i) {
16431                     dc.b4DragOver(e, overEvts[i].id);
16432                     dc.onDragOver(e, overEvts[i].id);
16433                 }
16434
16435                 // fire drop events
16436                 for (i=0, len=dropEvts.length; i<len; ++i) {
16437                     dc.b4DragDrop(e, dropEvts[i].id);
16438                     dc.onDragDrop(e, dropEvts[i].id);
16439                 }
16440
16441             }
16442
16443             // notify about a drop that did not find a target
16444             if (isDrop && !dropEvts.length) {
16445                 dc.onInvalidDrop(e);
16446             }
16447
16448         },
16449
16450         /**
16451          * Helper function for getting the best match from the list of drag
16452          * and drop objects returned by the drag and drop events when we are
16453          * in INTERSECT mode.  It returns either the first object that the
16454          * cursor is over, or the object that has the greatest overlap with
16455          * the dragged element.
16456          * @method getBestMatch
16457          * @param  {DragDrop[]} dds The array of drag and drop objects
16458          * targeted
16459          * @return {DragDrop}       The best single match
16460          * @static
16461          */
16462         getBestMatch: function(dds) {
16463             var winner = null;
16464             // Return null if the input is not what we expect
16465             //if (!dds || !dds.length || dds.length == 0) {
16466                // winner = null;
16467             // If there is only one item, it wins
16468             //} else if (dds.length == 1) {
16469
16470             var len = dds.length;
16471
16472             if (len == 1) {
16473                 winner = dds[0];
16474             } else {
16475                 // Loop through the targeted items
16476                 for (var i=0; i<len; ++i) {
16477                     var dd = dds[i];
16478                     // If the cursor is over the object, it wins.  If the
16479                     // cursor is over multiple matches, the first one we come
16480                     // to wins.
16481                     if (dd.cursorIsOver) {
16482                         winner = dd;
16483                         break;
16484                     // Otherwise the object with the most overlap wins
16485                     } else {
16486                         if (!winner ||
16487                             winner.overlap.getArea() < dd.overlap.getArea()) {
16488                             winner = dd;
16489                         }
16490                     }
16491                 }
16492             }
16493
16494             return winner;
16495         },
16496
16497         /**
16498          * Refreshes the cache of the top-left and bottom-right points of the
16499          * drag and drop objects in the specified group(s).  This is in the
16500          * format that is stored in the drag and drop instance, so typical
16501          * usage is:
16502          * <code>
16503          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
16504          * </code>
16505          * Alternatively:
16506          * <code>
16507          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
16508          * </code>
16509          * @TODO this really should be an indexed array.  Alternatively this
16510          * method could accept both.
16511          * @method refreshCache
16512          * @param {Object} groups an associative array of groups to refresh
16513          * @static
16514          */
16515         refreshCache: function(groups) {
16516             for (var sGroup in groups) {
16517                 if ("string" != typeof sGroup) {
16518                     continue;
16519                 }
16520                 for (var i in this.ids[sGroup]) {
16521                     var oDD = this.ids[sGroup][i];
16522
16523                     if (this.isTypeOfDD(oDD)) {
16524                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
16525                         var loc = this.getLocation(oDD);
16526                         if (loc) {
16527                             this.locationCache[oDD.id] = loc;
16528                         } else {
16529                             delete this.locationCache[oDD.id];
16530                             // this will unregister the drag and drop object if
16531                             // the element is not in a usable state
16532                             // oDD.unreg();
16533                         }
16534                     }
16535                 }
16536             }
16537         },
16538
16539         /**
16540          * This checks to make sure an element exists and is in the DOM.  The
16541          * main purpose is to handle cases where innerHTML is used to remove
16542          * drag and drop objects from the DOM.  IE provides an 'unspecified
16543          * error' when trying to access the offsetParent of such an element
16544          * @method verifyEl
16545          * @param {HTMLElement} el the element to check
16546          * @return {boolean} true if the element looks usable
16547          * @static
16548          */
16549         verifyEl: function(el) {
16550             if (el) {
16551                 var parent;
16552                 if(Roo.isIE){
16553                     try{
16554                         parent = el.offsetParent;
16555                     }catch(e){}
16556                 }else{
16557                     parent = el.offsetParent;
16558                 }
16559                 if (parent) {
16560                     return true;
16561                 }
16562             }
16563
16564             return false;
16565         },
16566
16567         /**
16568          * Returns a Region object containing the drag and drop element's position
16569          * and size, including the padding configured for it
16570          * @method getLocation
16571          * @param {DragDrop} oDD the drag and drop object to get the
16572          *                       location for
16573          * @return {Roo.lib.Region} a Region object representing the total area
16574          *                             the element occupies, including any padding
16575          *                             the instance is configured for.
16576          * @static
16577          */
16578         getLocation: function(oDD) {
16579             if (! this.isTypeOfDD(oDD)) {
16580                 return null;
16581             }
16582
16583             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
16584
16585             try {
16586                 pos= Roo.lib.Dom.getXY(el);
16587             } catch (e) { }
16588
16589             if (!pos) {
16590                 return null;
16591             }
16592
16593             x1 = pos[0];
16594             x2 = x1 + el.offsetWidth;
16595             y1 = pos[1];
16596             y2 = y1 + el.offsetHeight;
16597
16598             t = y1 - oDD.padding[0];
16599             r = x2 + oDD.padding[1];
16600             b = y2 + oDD.padding[2];
16601             l = x1 - oDD.padding[3];
16602
16603             return new Roo.lib.Region( t, r, b, l );
16604         },
16605
16606         /**
16607          * Checks the cursor location to see if it over the target
16608          * @method isOverTarget
16609          * @param {Roo.lib.Point} pt The point to evaluate
16610          * @param {DragDrop} oTarget the DragDrop object we are inspecting
16611          * @return {boolean} true if the mouse is over the target
16612          * @private
16613          * @static
16614          */
16615         isOverTarget: function(pt, oTarget, intersect) {
16616             // use cache if available
16617             var loc = this.locationCache[oTarget.id];
16618             if (!loc || !this.useCache) {
16619                 loc = this.getLocation(oTarget);
16620                 this.locationCache[oTarget.id] = loc;
16621
16622             }
16623
16624             if (!loc) {
16625                 return false;
16626             }
16627
16628             oTarget.cursorIsOver = loc.contains( pt );
16629
16630             // DragDrop is using this as a sanity check for the initial mousedown
16631             // in this case we are done.  In POINT mode, if the drag obj has no
16632             // contraints, we are also done. Otherwise we need to evaluate the
16633             // location of the target as related to the actual location of the
16634             // dragged element.
16635             var dc = this.dragCurrent;
16636             if (!dc || !dc.getTargetCoord ||
16637                     (!intersect && !dc.constrainX && !dc.constrainY)) {
16638                 return oTarget.cursorIsOver;
16639             }
16640
16641             oTarget.overlap = null;
16642
16643             // Get the current location of the drag element, this is the
16644             // location of the mouse event less the delta that represents
16645             // where the original mousedown happened on the element.  We
16646             // need to consider constraints and ticks as well.
16647             var pos = dc.getTargetCoord(pt.x, pt.y);
16648
16649             var el = dc.getDragEl();
16650             var curRegion = new Roo.lib.Region( pos.y,
16651                                                    pos.x + el.offsetWidth,
16652                                                    pos.y + el.offsetHeight,
16653                                                    pos.x );
16654
16655             var overlap = curRegion.intersect(loc);
16656
16657             if (overlap) {
16658                 oTarget.overlap = overlap;
16659                 return (intersect) ? true : oTarget.cursorIsOver;
16660             } else {
16661                 return false;
16662             }
16663         },
16664
16665         /**
16666          * unload event handler
16667          * @method _onUnload
16668          * @private
16669          * @static
16670          */
16671         _onUnload: function(e, me) {
16672             Roo.dd.DragDropMgr.unregAll();
16673         },
16674
16675         /**
16676          * Cleans up the drag and drop events and objects.
16677          * @method unregAll
16678          * @private
16679          * @static
16680          */
16681         unregAll: function() {
16682
16683             if (this.dragCurrent) {
16684                 this.stopDrag();
16685                 this.dragCurrent = null;
16686             }
16687
16688             this._execOnAll("unreg", []);
16689
16690             for (i in this.elementCache) {
16691                 delete this.elementCache[i];
16692             }
16693
16694             this.elementCache = {};
16695             this.ids = {};
16696         },
16697
16698         /**
16699          * A cache of DOM elements
16700          * @property elementCache
16701          * @private
16702          * @static
16703          */
16704         elementCache: {},
16705
16706         /**
16707          * Get the wrapper for the DOM element specified
16708          * @method getElWrapper
16709          * @param {String} id the id of the element to get
16710          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
16711          * @private
16712          * @deprecated This wrapper isn't that useful
16713          * @static
16714          */
16715         getElWrapper: function(id) {
16716             var oWrapper = this.elementCache[id];
16717             if (!oWrapper || !oWrapper.el) {
16718                 oWrapper = this.elementCache[id] =
16719                     new this.ElementWrapper(Roo.getDom(id));
16720             }
16721             return oWrapper;
16722         },
16723
16724         /**
16725          * Returns the actual DOM element
16726          * @method getElement
16727          * @param {String} id the id of the elment to get
16728          * @return {Object} The element
16729          * @deprecated use Roo.getDom instead
16730          * @static
16731          */
16732         getElement: function(id) {
16733             return Roo.getDom(id);
16734         },
16735
16736         /**
16737          * Returns the style property for the DOM element (i.e.,
16738          * document.getElById(id).style)
16739          * @method getCss
16740          * @param {String} id the id of the elment to get
16741          * @return {Object} The style property of the element
16742          * @deprecated use Roo.getDom instead
16743          * @static
16744          */
16745         getCss: function(id) {
16746             var el = Roo.getDom(id);
16747             return (el) ? el.style : null;
16748         },
16749
16750         /**
16751          * Inner class for cached elements
16752          * @class DragDropMgr.ElementWrapper
16753          * @for DragDropMgr
16754          * @private
16755          * @deprecated
16756          */
16757         ElementWrapper: function(el) {
16758                 /**
16759                  * The element
16760                  * @property el
16761                  */
16762                 this.el = el || null;
16763                 /**
16764                  * The element id
16765                  * @property id
16766                  */
16767                 this.id = this.el && el.id;
16768                 /**
16769                  * A reference to the style property
16770                  * @property css
16771                  */
16772                 this.css = this.el && el.style;
16773             },
16774
16775         /**
16776          * Returns the X position of an html element
16777          * @method getPosX
16778          * @param el the element for which to get the position
16779          * @return {int} the X coordinate
16780          * @for DragDropMgr
16781          * @deprecated use Roo.lib.Dom.getX instead
16782          * @static
16783          */
16784         getPosX: function(el) {
16785             return Roo.lib.Dom.getX(el);
16786         },
16787
16788         /**
16789          * Returns the Y position of an html element
16790          * @method getPosY
16791          * @param el the element for which to get the position
16792          * @return {int} the Y coordinate
16793          * @deprecated use Roo.lib.Dom.getY instead
16794          * @static
16795          */
16796         getPosY: function(el) {
16797             return Roo.lib.Dom.getY(el);
16798         },
16799
16800         /**
16801          * Swap two nodes.  In IE, we use the native method, for others we
16802          * emulate the IE behavior
16803          * @method swapNode
16804          * @param n1 the first node to swap
16805          * @param n2 the other node to swap
16806          * @static
16807          */
16808         swapNode: function(n1, n2) {
16809             if (n1.swapNode) {
16810                 n1.swapNode(n2);
16811             } else {
16812                 var p = n2.parentNode;
16813                 var s = n2.nextSibling;
16814
16815                 if (s == n1) {
16816                     p.insertBefore(n1, n2);
16817                 } else if (n2 == n1.nextSibling) {
16818                     p.insertBefore(n2, n1);
16819                 } else {
16820                     n1.parentNode.replaceChild(n2, n1);
16821                     p.insertBefore(n1, s);
16822                 }
16823             }
16824         },
16825
16826         /**
16827          * Returns the current scroll position
16828          * @method getScroll
16829          * @private
16830          * @static
16831          */
16832         getScroll: function () {
16833             var t, l, dde=document.documentElement, db=document.body;
16834             if (dde && (dde.scrollTop || dde.scrollLeft)) {
16835                 t = dde.scrollTop;
16836                 l = dde.scrollLeft;
16837             } else if (db) {
16838                 t = db.scrollTop;
16839                 l = db.scrollLeft;
16840             } else {
16841
16842             }
16843             return { top: t, left: l };
16844         },
16845
16846         /**
16847          * Returns the specified element style property
16848          * @method getStyle
16849          * @param {HTMLElement} el          the element
16850          * @param {string}      styleProp   the style property
16851          * @return {string} The value of the style property
16852          * @deprecated use Roo.lib.Dom.getStyle
16853          * @static
16854          */
16855         getStyle: function(el, styleProp) {
16856             return Roo.fly(el).getStyle(styleProp);
16857         },
16858
16859         /**
16860          * Gets the scrollTop
16861          * @method getScrollTop
16862          * @return {int} the document's scrollTop
16863          * @static
16864          */
16865         getScrollTop: function () { return this.getScroll().top; },
16866
16867         /**
16868          * Gets the scrollLeft
16869          * @method getScrollLeft
16870          * @return {int} the document's scrollTop
16871          * @static
16872          */
16873         getScrollLeft: function () { return this.getScroll().left; },
16874
16875         /**
16876          * Sets the x/y position of an element to the location of the
16877          * target element.
16878          * @method moveToEl
16879          * @param {HTMLElement} moveEl      The element to move
16880          * @param {HTMLElement} targetEl    The position reference element
16881          * @static
16882          */
16883         moveToEl: function (moveEl, targetEl) {
16884             var aCoord = Roo.lib.Dom.getXY(targetEl);
16885             Roo.lib.Dom.setXY(moveEl, aCoord);
16886         },
16887
16888         /**
16889          * Numeric array sort function
16890          * @method numericSort
16891          * @static
16892          */
16893         numericSort: function(a, b) { return (a - b); },
16894
16895         /**
16896          * Internal counter
16897          * @property _timeoutCount
16898          * @private
16899          * @static
16900          */
16901         _timeoutCount: 0,
16902
16903         /**
16904          * Trying to make the load order less important.  Without this we get
16905          * an error if this file is loaded before the Event Utility.
16906          * @method _addListeners
16907          * @private
16908          * @static
16909          */
16910         _addListeners: function() {
16911             var DDM = Roo.dd.DDM;
16912             if ( Roo.lib.Event && document ) {
16913                 DDM._onLoad();
16914             } else {
16915                 if (DDM._timeoutCount > 2000) {
16916                 } else {
16917                     setTimeout(DDM._addListeners, 10);
16918                     if (document && document.body) {
16919                         DDM._timeoutCount += 1;
16920                     }
16921                 }
16922             }
16923         },
16924
16925         /**
16926          * Recursively searches the immediate parent and all child nodes for
16927          * the handle element in order to determine wheter or not it was
16928          * clicked.
16929          * @method handleWasClicked
16930          * @param node the html element to inspect
16931          * @static
16932          */
16933         handleWasClicked: function(node, id) {
16934             if (this.isHandle(id, node.id)) {
16935                 return true;
16936             } else {
16937                 // check to see if this is a text node child of the one we want
16938                 var p = node.parentNode;
16939
16940                 while (p) {
16941                     if (this.isHandle(id, p.id)) {
16942                         return true;
16943                     } else {
16944                         p = p.parentNode;
16945                     }
16946                 }
16947             }
16948
16949             return false;
16950         }
16951
16952     };
16953
16954 }();
16955
16956 // shorter alias, save a few bytes
16957 Roo.dd.DDM = Roo.dd.DragDropMgr;
16958 Roo.dd.DDM._addListeners();
16959
16960 }/*
16961  * Based on:
16962  * Ext JS Library 1.1.1
16963  * Copyright(c) 2006-2007, Ext JS, LLC.
16964  *
16965  * Originally Released Under LGPL - original licence link has changed is not relivant.
16966  *
16967  * Fork - LGPL
16968  * <script type="text/javascript">
16969  */
16970
16971 /**
16972  * @class Roo.dd.DD
16973  * A DragDrop implementation where the linked element follows the
16974  * mouse cursor during a drag.
16975  * @extends Roo.dd.DragDrop
16976  * @constructor
16977  * @param {String} id the id of the linked element
16978  * @param {String} sGroup the group of related DragDrop items
16979  * @param {object} config an object containing configurable attributes
16980  *                Valid properties for DD:
16981  *                    scroll
16982  */
16983 Roo.dd.DD = function(id, sGroup, config) {
16984     if (id) {
16985         this.init(id, sGroup, config);
16986     }
16987 };
16988
16989 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
16990
16991     /**
16992      * When set to true, the utility automatically tries to scroll the browser
16993      * window wehn a drag and drop element is dragged near the viewport boundary.
16994      * Defaults to true.
16995      * @property scroll
16996      * @type boolean
16997      */
16998     scroll: true,
16999
17000     /**
17001      * Sets the pointer offset to the distance between the linked element's top
17002      * left corner and the location the element was clicked
17003      * @method autoOffset
17004      * @param {int} iPageX the X coordinate of the click
17005      * @param {int} iPageY the Y coordinate of the click
17006      */
17007     autoOffset: function(iPageX, iPageY) {
17008         var x = iPageX - this.startPageX;
17009         var y = iPageY - this.startPageY;
17010         this.setDelta(x, y);
17011     },
17012
17013     /**
17014      * Sets the pointer offset.  You can call this directly to force the
17015      * offset to be in a particular location (e.g., pass in 0,0 to set it
17016      * to the center of the object)
17017      * @method setDelta
17018      * @param {int} iDeltaX the distance from the left
17019      * @param {int} iDeltaY the distance from the top
17020      */
17021     setDelta: function(iDeltaX, iDeltaY) {
17022         this.deltaX = iDeltaX;
17023         this.deltaY = iDeltaY;
17024     },
17025
17026     /**
17027      * Sets the drag element to the location of the mousedown or click event,
17028      * maintaining the cursor location relative to the location on the element
17029      * that was clicked.  Override this if you want to place the element in a
17030      * location other than where the cursor is.
17031      * @method setDragElPos
17032      * @param {int} iPageX the X coordinate of the mousedown or drag event
17033      * @param {int} iPageY the Y coordinate of the mousedown or drag event
17034      */
17035     setDragElPos: function(iPageX, iPageY) {
17036         // the first time we do this, we are going to check to make sure
17037         // the element has css positioning
17038
17039         var el = this.getDragEl();
17040         this.alignElWithMouse(el, iPageX, iPageY);
17041     },
17042
17043     /**
17044      * Sets the element to the location of the mousedown or click event,
17045      * maintaining the cursor location relative to the location on the element
17046      * that was clicked.  Override this if you want to place the element in a
17047      * location other than where the cursor is.
17048      * @method alignElWithMouse
17049      * @param {HTMLElement} el the element to move
17050      * @param {int} iPageX the X coordinate of the mousedown or drag event
17051      * @param {int} iPageY the Y coordinate of the mousedown or drag event
17052      */
17053     alignElWithMouse: function(el, iPageX, iPageY) {
17054         var oCoord = this.getTargetCoord(iPageX, iPageY);
17055         var fly = el.dom ? el : Roo.fly(el);
17056         if (!this.deltaSetXY) {
17057             var aCoord = [oCoord.x, oCoord.y];
17058             fly.setXY(aCoord);
17059             var newLeft = fly.getLeft(true);
17060             var newTop  = fly.getTop(true);
17061             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
17062         } else {
17063             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
17064         }
17065
17066         this.cachePosition(oCoord.x, oCoord.y);
17067         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
17068         return oCoord;
17069     },
17070
17071     /**
17072      * Saves the most recent position so that we can reset the constraints and
17073      * tick marks on-demand.  We need to know this so that we can calculate the
17074      * number of pixels the element is offset from its original position.
17075      * @method cachePosition
17076      * @param iPageX the current x position (optional, this just makes it so we
17077      * don't have to look it up again)
17078      * @param iPageY the current y position (optional, this just makes it so we
17079      * don't have to look it up again)
17080      */
17081     cachePosition: function(iPageX, iPageY) {
17082         if (iPageX) {
17083             this.lastPageX = iPageX;
17084             this.lastPageY = iPageY;
17085         } else {
17086             var aCoord = Roo.lib.Dom.getXY(this.getEl());
17087             this.lastPageX = aCoord[0];
17088             this.lastPageY = aCoord[1];
17089         }
17090     },
17091
17092     /**
17093      * Auto-scroll the window if the dragged object has been moved beyond the
17094      * visible window boundary.
17095      * @method autoScroll
17096      * @param {int} x the drag element's x position
17097      * @param {int} y the drag element's y position
17098      * @param {int} h the height of the drag element
17099      * @param {int} w the width of the drag element
17100      * @private
17101      */
17102     autoScroll: function(x, y, h, w) {
17103
17104         if (this.scroll) {
17105             // The client height
17106             var clientH = Roo.lib.Dom.getViewWidth();
17107
17108             // The client width
17109             var clientW = Roo.lib.Dom.getViewHeight();
17110
17111             // The amt scrolled down
17112             var st = this.DDM.getScrollTop();
17113
17114             // The amt scrolled right
17115             var sl = this.DDM.getScrollLeft();
17116
17117             // Location of the bottom of the element
17118             var bot = h + y;
17119
17120             // Location of the right of the element
17121             var right = w + x;
17122
17123             // The distance from the cursor to the bottom of the visible area,
17124             // adjusted so that we don't scroll if the cursor is beyond the
17125             // element drag constraints
17126             var toBot = (clientH + st - y - this.deltaY);
17127
17128             // The distance from the cursor to the right of the visible area
17129             var toRight = (clientW + sl - x - this.deltaX);
17130
17131
17132             // How close to the edge the cursor must be before we scroll
17133             // var thresh = (document.all) ? 100 : 40;
17134             var thresh = 40;
17135
17136             // How many pixels to scroll per autoscroll op.  This helps to reduce
17137             // clunky scrolling. IE is more sensitive about this ... it needs this
17138             // value to be higher.
17139             var scrAmt = (document.all) ? 80 : 30;
17140
17141             // Scroll down if we are near the bottom of the visible page and the
17142             // obj extends below the crease
17143             if ( bot > clientH && toBot < thresh ) {
17144                 window.scrollTo(sl, st + scrAmt);
17145             }
17146
17147             // Scroll up if the window is scrolled down and the top of the object
17148             // goes above the top border
17149             if ( y < st && st > 0 && y - st < thresh ) {
17150                 window.scrollTo(sl, st - scrAmt);
17151             }
17152
17153             // Scroll right if the obj is beyond the right border and the cursor is
17154             // near the border.
17155             if ( right > clientW && toRight < thresh ) {
17156                 window.scrollTo(sl + scrAmt, st);
17157             }
17158
17159             // Scroll left if the window has been scrolled to the right and the obj
17160             // extends past the left border
17161             if ( x < sl && sl > 0 && x - sl < thresh ) {
17162                 window.scrollTo(sl - scrAmt, st);
17163             }
17164         }
17165     },
17166
17167     /**
17168      * Finds the location the element should be placed if we want to move
17169      * it to where the mouse location less the click offset would place us.
17170      * @method getTargetCoord
17171      * @param {int} iPageX the X coordinate of the click
17172      * @param {int} iPageY the Y coordinate of the click
17173      * @return an object that contains the coordinates (Object.x and Object.y)
17174      * @private
17175      */
17176     getTargetCoord: function(iPageX, iPageY) {
17177
17178
17179         var x = iPageX - this.deltaX;
17180         var y = iPageY - this.deltaY;
17181
17182         if (this.constrainX) {
17183             if (x < this.minX) { x = this.minX; }
17184             if (x > this.maxX) { x = this.maxX; }
17185         }
17186
17187         if (this.constrainY) {
17188             if (y < this.minY) { y = this.minY; }
17189             if (y > this.maxY) { y = this.maxY; }
17190         }
17191
17192         x = this.getTick(x, this.xTicks);
17193         y = this.getTick(y, this.yTicks);
17194
17195
17196         return {x:x, y:y};
17197     },
17198
17199     /*
17200      * Sets up config options specific to this class. Overrides
17201      * Roo.dd.DragDrop, but all versions of this method through the
17202      * inheritance chain are called
17203      */
17204     applyConfig: function() {
17205         Roo.dd.DD.superclass.applyConfig.call(this);
17206         this.scroll = (this.config.scroll !== false);
17207     },
17208
17209     /*
17210      * Event that fires prior to the onMouseDown event.  Overrides
17211      * Roo.dd.DragDrop.
17212      */
17213     b4MouseDown: function(e) {
17214         // this.resetConstraints();
17215         this.autoOffset(e.getPageX(),
17216                             e.getPageY());
17217     },
17218
17219     /*
17220      * Event that fires prior to the onDrag event.  Overrides
17221      * Roo.dd.DragDrop.
17222      */
17223     b4Drag: function(e) {
17224         this.setDragElPos(e.getPageX(),
17225                             e.getPageY());
17226     },
17227
17228     toString: function() {
17229         return ("DD " + this.id);
17230     }
17231
17232     //////////////////////////////////////////////////////////////////////////
17233     // Debugging ygDragDrop events that can be overridden
17234     //////////////////////////////////////////////////////////////////////////
17235     /*
17236     startDrag: function(x, y) {
17237     },
17238
17239     onDrag: function(e) {
17240     },
17241
17242     onDragEnter: function(e, id) {
17243     },
17244
17245     onDragOver: function(e, id) {
17246     },
17247
17248     onDragOut: function(e, id) {
17249     },
17250
17251     onDragDrop: function(e, id) {
17252     },
17253
17254     endDrag: function(e) {
17255     }
17256
17257     */
17258
17259 });/*
17260  * Based on:
17261  * Ext JS Library 1.1.1
17262  * Copyright(c) 2006-2007, Ext JS, LLC.
17263  *
17264  * Originally Released Under LGPL - original licence link has changed is not relivant.
17265  *
17266  * Fork - LGPL
17267  * <script type="text/javascript">
17268  */
17269
17270 /**
17271  * @class Roo.dd.DDProxy
17272  * A DragDrop implementation that inserts an empty, bordered div into
17273  * the document that follows the cursor during drag operations.  At the time of
17274  * the click, the frame div is resized to the dimensions of the linked html
17275  * element, and moved to the exact location of the linked element.
17276  *
17277  * References to the "frame" element refer to the single proxy element that
17278  * was created to be dragged in place of all DDProxy elements on the
17279  * page.
17280  *
17281  * @extends Roo.dd.DD
17282  * @constructor
17283  * @param {String} id the id of the linked html element
17284  * @param {String} sGroup the group of related DragDrop objects
17285  * @param {object} config an object containing configurable attributes
17286  *                Valid properties for DDProxy in addition to those in DragDrop:
17287  *                   resizeFrame, centerFrame, dragElId
17288  */
17289 Roo.dd.DDProxy = function(id, sGroup, config) {
17290     if (id) {
17291         this.init(id, sGroup, config);
17292         this.initFrame();
17293     }
17294 };
17295
17296 /**
17297  * The default drag frame div id
17298  * @property Roo.dd.DDProxy.dragElId
17299  * @type String
17300  * @static
17301  */
17302 Roo.dd.DDProxy.dragElId = "ygddfdiv";
17303
17304 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
17305
17306     /**
17307      * By default we resize the drag frame to be the same size as the element
17308      * we want to drag (this is to get the frame effect).  We can turn it off
17309      * if we want a different behavior.
17310      * @property resizeFrame
17311      * @type boolean
17312      */
17313     resizeFrame: true,
17314
17315     /**
17316      * By default the frame is positioned exactly where the drag element is, so
17317      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
17318      * you do not have constraints on the obj is to have the drag frame centered
17319      * around the cursor.  Set centerFrame to true for this effect.
17320      * @property centerFrame
17321      * @type boolean
17322      */
17323     centerFrame: false,
17324
17325     /**
17326      * Creates the proxy element if it does not yet exist
17327      * @method createFrame
17328      */
17329     createFrame: function() {
17330         var self = this;
17331         var body = document.body;
17332
17333         if (!body || !body.firstChild) {
17334             setTimeout( function() { self.createFrame(); }, 50 );
17335             return;
17336         }
17337
17338         var div = this.getDragEl();
17339
17340         if (!div) {
17341             div    = document.createElement("div");
17342             div.id = this.dragElId;
17343             var s  = div.style;
17344
17345             s.position   = "absolute";
17346             s.visibility = "hidden";
17347             s.cursor     = "move";
17348             s.border     = "2px solid #aaa";
17349             s.zIndex     = 999;
17350
17351             // appendChild can blow up IE if invoked prior to the window load event
17352             // while rendering a table.  It is possible there are other scenarios
17353             // that would cause this to happen as well.
17354             body.insertBefore(div, body.firstChild);
17355         }
17356     },
17357
17358     /**
17359      * Initialization for the drag frame element.  Must be called in the
17360      * constructor of all subclasses
17361      * @method initFrame
17362      */
17363     initFrame: function() {
17364         this.createFrame();
17365     },
17366
17367     applyConfig: function() {
17368         Roo.dd.DDProxy.superclass.applyConfig.call(this);
17369
17370         this.resizeFrame = (this.config.resizeFrame !== false);
17371         this.centerFrame = (this.config.centerFrame);
17372         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
17373     },
17374
17375     /**
17376      * Resizes the drag frame to the dimensions of the clicked object, positions
17377      * it over the object, and finally displays it
17378      * @method showFrame
17379      * @param {int} iPageX X click position
17380      * @param {int} iPageY Y click position
17381      * @private
17382      */
17383     showFrame: function(iPageX, iPageY) {
17384         var el = this.getEl();
17385         var dragEl = this.getDragEl();
17386         var s = dragEl.style;
17387
17388         this._resizeProxy();
17389
17390         if (this.centerFrame) {
17391             this.setDelta( Math.round(parseInt(s.width,  10)/2),
17392                            Math.round(parseInt(s.height, 10)/2) );
17393         }
17394
17395         this.setDragElPos(iPageX, iPageY);
17396
17397         Roo.fly(dragEl).show();
17398     },
17399
17400     /**
17401      * The proxy is automatically resized to the dimensions of the linked
17402      * element when a drag is initiated, unless resizeFrame is set to false
17403      * @method _resizeProxy
17404      * @private
17405      */
17406     _resizeProxy: function() {
17407         if (this.resizeFrame) {
17408             var el = this.getEl();
17409             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
17410         }
17411     },
17412
17413     // overrides Roo.dd.DragDrop
17414     b4MouseDown: function(e) {
17415         var x = e.getPageX();
17416         var y = e.getPageY();
17417         this.autoOffset(x, y);
17418         this.setDragElPos(x, y);
17419     },
17420
17421     // overrides Roo.dd.DragDrop
17422     b4StartDrag: function(x, y) {
17423         // show the drag frame
17424         this.showFrame(x, y);
17425     },
17426
17427     // overrides Roo.dd.DragDrop
17428     b4EndDrag: function(e) {
17429         Roo.fly(this.getDragEl()).hide();
17430     },
17431
17432     // overrides Roo.dd.DragDrop
17433     // By default we try to move the element to the last location of the frame.
17434     // This is so that the default behavior mirrors that of Roo.dd.DD.
17435     endDrag: function(e) {
17436
17437         var lel = this.getEl();
17438         var del = this.getDragEl();
17439
17440         // Show the drag frame briefly so we can get its position
17441         del.style.visibility = "";
17442
17443         this.beforeMove();
17444         // Hide the linked element before the move to get around a Safari
17445         // rendering bug.
17446         lel.style.visibility = "hidden";
17447         Roo.dd.DDM.moveToEl(lel, del);
17448         del.style.visibility = "hidden";
17449         lel.style.visibility = "";
17450
17451         this.afterDrag();
17452     },
17453
17454     beforeMove : function(){
17455
17456     },
17457
17458     afterDrag : function(){
17459
17460     },
17461
17462     toString: function() {
17463         return ("DDProxy " + this.id);
17464     }
17465
17466 });
17467 /*
17468  * Based on:
17469  * Ext JS Library 1.1.1
17470  * Copyright(c) 2006-2007, Ext JS, LLC.
17471  *
17472  * Originally Released Under LGPL - original licence link has changed is not relivant.
17473  *
17474  * Fork - LGPL
17475  * <script type="text/javascript">
17476  */
17477
17478  /**
17479  * @class Roo.dd.DDTarget
17480  * A DragDrop implementation that does not move, but can be a drop
17481  * target.  You would get the same result by simply omitting implementation
17482  * for the event callbacks, but this way we reduce the processing cost of the
17483  * event listener and the callbacks.
17484  * @extends Roo.dd.DragDrop
17485  * @constructor
17486  * @param {String} id the id of the element that is a drop target
17487  * @param {String} sGroup the group of related DragDrop objects
17488  * @param {object} config an object containing configurable attributes
17489  *                 Valid properties for DDTarget in addition to those in
17490  *                 DragDrop:
17491  *                    none
17492  */
17493 Roo.dd.DDTarget = function(id, sGroup, config) {
17494     if (id) {
17495         this.initTarget(id, sGroup, config);
17496     }
17497     if (config.listeners || config.events) { 
17498        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
17499             listeners : config.listeners || {}, 
17500             events : config.events || {} 
17501         });    
17502     }
17503 };
17504
17505 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
17506 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
17507     toString: function() {
17508         return ("DDTarget " + this.id);
17509     }
17510 });
17511 /*
17512  * Based on:
17513  * Ext JS Library 1.1.1
17514  * Copyright(c) 2006-2007, Ext JS, LLC.
17515  *
17516  * Originally Released Under LGPL - original licence link has changed is not relivant.
17517  *
17518  * Fork - LGPL
17519  * <script type="text/javascript">
17520  */
17521  
17522
17523 /**
17524  * @class Roo.dd.ScrollManager
17525  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
17526  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
17527  * @singleton
17528  */
17529 Roo.dd.ScrollManager = function(){
17530     var ddm = Roo.dd.DragDropMgr;
17531     var els = {};
17532     var dragEl = null;
17533     var proc = {};
17534     
17535     var onStop = function(e){
17536         dragEl = null;
17537         clearProc();
17538     };
17539     
17540     var triggerRefresh = function(){
17541         if(ddm.dragCurrent){
17542              ddm.refreshCache(ddm.dragCurrent.groups);
17543         }
17544     };
17545     
17546     var doScroll = function(){
17547         if(ddm.dragCurrent){
17548             var dds = Roo.dd.ScrollManager;
17549             if(!dds.animate){
17550                 if(proc.el.scroll(proc.dir, dds.increment)){
17551                     triggerRefresh();
17552                 }
17553             }else{
17554                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
17555             }
17556         }
17557     };
17558     
17559     var clearProc = function(){
17560         if(proc.id){
17561             clearInterval(proc.id);
17562         }
17563         proc.id = 0;
17564         proc.el = null;
17565         proc.dir = "";
17566     };
17567     
17568     var startProc = function(el, dir){
17569         clearProc();
17570         proc.el = el;
17571         proc.dir = dir;
17572         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
17573     };
17574     
17575     var onFire = function(e, isDrop){
17576         if(isDrop || !ddm.dragCurrent){ return; }
17577         var dds = Roo.dd.ScrollManager;
17578         if(!dragEl || dragEl != ddm.dragCurrent){
17579             dragEl = ddm.dragCurrent;
17580             // refresh regions on drag start
17581             dds.refreshCache();
17582         }
17583         
17584         var xy = Roo.lib.Event.getXY(e);
17585         var pt = new Roo.lib.Point(xy[0], xy[1]);
17586         for(var id in els){
17587             var el = els[id], r = el._region;
17588             if(r && r.contains(pt) && el.isScrollable()){
17589                 if(r.bottom - pt.y <= dds.thresh){
17590                     if(proc.el != el){
17591                         startProc(el, "down");
17592                     }
17593                     return;
17594                 }else if(r.right - pt.x <= dds.thresh){
17595                     if(proc.el != el){
17596                         startProc(el, "left");
17597                     }
17598                     return;
17599                 }else if(pt.y - r.top <= dds.thresh){
17600                     if(proc.el != el){
17601                         startProc(el, "up");
17602                     }
17603                     return;
17604                 }else if(pt.x - r.left <= dds.thresh){
17605                     if(proc.el != el){
17606                         startProc(el, "right");
17607                     }
17608                     return;
17609                 }
17610             }
17611         }
17612         clearProc();
17613     };
17614     
17615     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
17616     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
17617     
17618     return {
17619         /**
17620          * Registers new overflow element(s) to auto scroll
17621          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
17622          */
17623         register : function(el){
17624             if(el instanceof Array){
17625                 for(var i = 0, len = el.length; i < len; i++) {
17626                         this.register(el[i]);
17627                 }
17628             }else{
17629                 el = Roo.get(el);
17630                 els[el.id] = el;
17631             }
17632         },
17633         
17634         /**
17635          * Unregisters overflow element(s) so they are no longer scrolled
17636          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
17637          */
17638         unregister : function(el){
17639             if(el instanceof Array){
17640                 for(var i = 0, len = el.length; i < len; i++) {
17641                         this.unregister(el[i]);
17642                 }
17643             }else{
17644                 el = Roo.get(el);
17645                 delete els[el.id];
17646             }
17647         },
17648         
17649         /**
17650          * The number of pixels from the edge of a container the pointer needs to be to 
17651          * trigger scrolling (defaults to 25)
17652          * @type Number
17653          */
17654         thresh : 25,
17655         
17656         /**
17657          * The number of pixels to scroll in each scroll increment (defaults to 50)
17658          * @type Number
17659          */
17660         increment : 100,
17661         
17662         /**
17663          * The frequency of scrolls in milliseconds (defaults to 500)
17664          * @type Number
17665          */
17666         frequency : 500,
17667         
17668         /**
17669          * True to animate the scroll (defaults to true)
17670          * @type Boolean
17671          */
17672         animate: true,
17673         
17674         /**
17675          * The animation duration in seconds - 
17676          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
17677          * @type Number
17678          */
17679         animDuration: .4,
17680         
17681         /**
17682          * Manually trigger a cache refresh.
17683          */
17684         refreshCache : function(){
17685             for(var id in els){
17686                 if(typeof els[id] == 'object'){ // for people extending the object prototype
17687                     els[id]._region = els[id].getRegion();
17688                 }
17689             }
17690         }
17691     };
17692 }();/*
17693  * Based on:
17694  * Ext JS Library 1.1.1
17695  * Copyright(c) 2006-2007, Ext JS, LLC.
17696  *
17697  * Originally Released Under LGPL - original licence link has changed is not relivant.
17698  *
17699  * Fork - LGPL
17700  * <script type="text/javascript">
17701  */
17702  
17703
17704 /**
17705  * @class Roo.dd.Registry
17706  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
17707  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
17708  * @singleton
17709  */
17710 Roo.dd.Registry = function(){
17711     var elements = {}; 
17712     var handles = {}; 
17713     var autoIdSeed = 0;
17714
17715     var getId = function(el, autogen){
17716         if(typeof el == "string"){
17717             return el;
17718         }
17719         var id = el.id;
17720         if(!id && autogen !== false){
17721             id = "roodd-" + (++autoIdSeed);
17722             el.id = id;
17723         }
17724         return id;
17725     };
17726     
17727     return {
17728     /**
17729      * Register a drag drop element
17730      * @param {String|HTMLElement} element The id or DOM node to register
17731      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
17732      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
17733      * knows how to interpret, plus there are some specific properties known to the Registry that should be
17734      * populated in the data object (if applicable):
17735      * <pre>
17736 Value      Description<br />
17737 ---------  ------------------------------------------<br />
17738 handles    Array of DOM nodes that trigger dragging<br />
17739            for the element being registered<br />
17740 isHandle   True if the element passed in triggers<br />
17741            dragging itself, else false
17742 </pre>
17743      */
17744         register : function(el, data){
17745             data = data || {};
17746             if(typeof el == "string"){
17747                 el = document.getElementById(el);
17748             }
17749             data.ddel = el;
17750             elements[getId(el)] = data;
17751             if(data.isHandle !== false){
17752                 handles[data.ddel.id] = data;
17753             }
17754             if(data.handles){
17755                 var hs = data.handles;
17756                 for(var i = 0, len = hs.length; i < len; i++){
17757                         handles[getId(hs[i])] = data;
17758                 }
17759             }
17760         },
17761
17762     /**
17763      * Unregister a drag drop element
17764      * @param {String|HTMLElement}  element The id or DOM node to unregister
17765      */
17766         unregister : function(el){
17767             var id = getId(el, false);
17768             var data = elements[id];
17769             if(data){
17770                 delete elements[id];
17771                 if(data.handles){
17772                     var hs = data.handles;
17773                     for(var i = 0, len = hs.length; i < len; i++){
17774                         delete handles[getId(hs[i], false)];
17775                     }
17776                 }
17777             }
17778         },
17779
17780     /**
17781      * Returns the handle registered for a DOM Node by id
17782      * @param {String|HTMLElement} id The DOM node or id to look up
17783      * @return {Object} handle The custom handle data
17784      */
17785         getHandle : function(id){
17786             if(typeof id != "string"){ // must be element?
17787                 id = id.id;
17788             }
17789             return handles[id];
17790         },
17791
17792     /**
17793      * Returns the handle that is registered for the DOM node that is the target of the event
17794      * @param {Event} e The event
17795      * @return {Object} handle The custom handle data
17796      */
17797         getHandleFromEvent : function(e){
17798             var t = Roo.lib.Event.getTarget(e);
17799             return t ? handles[t.id] : null;
17800         },
17801
17802     /**
17803      * Returns a custom data object that is registered for a DOM node by id
17804      * @param {String|HTMLElement} id The DOM node or id to look up
17805      * @return {Object} data The custom data
17806      */
17807         getTarget : function(id){
17808             if(typeof id != "string"){ // must be element?
17809                 id = id.id;
17810             }
17811             return elements[id];
17812         },
17813
17814     /**
17815      * Returns a custom data object that is registered for the DOM node that is the target of the event
17816      * @param {Event} e The event
17817      * @return {Object} data The custom data
17818      */
17819         getTargetFromEvent : function(e){
17820             var t = Roo.lib.Event.getTarget(e);
17821             return t ? elements[t.id] || handles[t.id] : null;
17822         }
17823     };
17824 }();/*
17825  * Based on:
17826  * Ext JS Library 1.1.1
17827  * Copyright(c) 2006-2007, Ext JS, LLC.
17828  *
17829  * Originally Released Under LGPL - original licence link has changed is not relivant.
17830  *
17831  * Fork - LGPL
17832  * <script type="text/javascript">
17833  */
17834  
17835
17836 /**
17837  * @class Roo.dd.StatusProxy
17838  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
17839  * default drag proxy used by all Roo.dd components.
17840  * @constructor
17841  * @param {Object} config
17842  */
17843 Roo.dd.StatusProxy = function(config){
17844     Roo.apply(this, config);
17845     this.id = this.id || Roo.id();
17846     this.el = new Roo.Layer({
17847         dh: {
17848             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
17849                 {tag: "div", cls: "x-dd-drop-icon"},
17850                 {tag: "div", cls: "x-dd-drag-ghost"}
17851             ]
17852         }, 
17853         shadow: !config || config.shadow !== false
17854     });
17855     this.ghost = Roo.get(this.el.dom.childNodes[1]);
17856     this.dropStatus = this.dropNotAllowed;
17857 };
17858
17859 Roo.dd.StatusProxy.prototype = {
17860     /**
17861      * @cfg {String} dropAllowed
17862      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
17863      */
17864     dropAllowed : "x-dd-drop-ok",
17865     /**
17866      * @cfg {String} dropNotAllowed
17867      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
17868      */
17869     dropNotAllowed : "x-dd-drop-nodrop",
17870
17871     /**
17872      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
17873      * over the current target element.
17874      * @param {String} cssClass The css class for the new drop status indicator image
17875      */
17876     setStatus : function(cssClass){
17877         cssClass = cssClass || this.dropNotAllowed;
17878         if(this.dropStatus != cssClass){
17879             this.el.replaceClass(this.dropStatus, cssClass);
17880             this.dropStatus = cssClass;
17881         }
17882     },
17883
17884     /**
17885      * Resets the status indicator to the default dropNotAllowed value
17886      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
17887      */
17888     reset : function(clearGhost){
17889         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
17890         this.dropStatus = this.dropNotAllowed;
17891         if(clearGhost){
17892             this.ghost.update("");
17893         }
17894     },
17895
17896     /**
17897      * Updates the contents of the ghost element
17898      * @param {String} html The html that will replace the current innerHTML of the ghost element
17899      */
17900     update : function(html){
17901         if(typeof html == "string"){
17902             this.ghost.update(html);
17903         }else{
17904             this.ghost.update("");
17905             html.style.margin = "0";
17906             this.ghost.dom.appendChild(html);
17907         }
17908         // ensure float = none set?? cant remember why though.
17909         var el = this.ghost.dom.firstChild;
17910                 if(el){
17911                         Roo.fly(el).setStyle('float', 'none');
17912                 }
17913     },
17914     
17915     /**
17916      * Returns the underlying proxy {@link Roo.Layer}
17917      * @return {Roo.Layer} el
17918     */
17919     getEl : function(){
17920         return this.el;
17921     },
17922
17923     /**
17924      * Returns the ghost element
17925      * @return {Roo.Element} el
17926      */
17927     getGhost : function(){
17928         return this.ghost;
17929     },
17930
17931     /**
17932      * Hides the proxy
17933      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
17934      */
17935     hide : function(clear){
17936         this.el.hide();
17937         if(clear){
17938             this.reset(true);
17939         }
17940     },
17941
17942     /**
17943      * Stops the repair animation if it's currently running
17944      */
17945     stop : function(){
17946         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
17947             this.anim.stop();
17948         }
17949     },
17950
17951     /**
17952      * Displays this proxy
17953      */
17954     show : function(){
17955         this.el.show();
17956     },
17957
17958     /**
17959      * Force the Layer to sync its shadow and shim positions to the element
17960      */
17961     sync : function(){
17962         this.el.sync();
17963     },
17964
17965     /**
17966      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
17967      * invalid drop operation by the item being dragged.
17968      * @param {Array} xy The XY position of the element ([x, y])
17969      * @param {Function} callback The function to call after the repair is complete
17970      * @param {Object} scope The scope in which to execute the callback
17971      */
17972     repair : function(xy, callback, scope){
17973         this.callback = callback;
17974         this.scope = scope;
17975         if(xy && this.animRepair !== false){
17976             this.el.addClass("x-dd-drag-repair");
17977             this.el.hideUnders(true);
17978             this.anim = this.el.shift({
17979                 duration: this.repairDuration || .5,
17980                 easing: 'easeOut',
17981                 xy: xy,
17982                 stopFx: true,
17983                 callback: this.afterRepair,
17984                 scope: this
17985             });
17986         }else{
17987             this.afterRepair();
17988         }
17989     },
17990
17991     // private
17992     afterRepair : function(){
17993         this.hide(true);
17994         if(typeof this.callback == "function"){
17995             this.callback.call(this.scope || this);
17996         }
17997         this.callback = null;
17998         this.scope = null;
17999     }
18000 };/*
18001  * Based on:
18002  * Ext JS Library 1.1.1
18003  * Copyright(c) 2006-2007, Ext JS, LLC.
18004  *
18005  * Originally Released Under LGPL - original licence link has changed is not relivant.
18006  *
18007  * Fork - LGPL
18008  * <script type="text/javascript">
18009  */
18010
18011 /**
18012  * @class Roo.dd.DragSource
18013  * @extends Roo.dd.DDProxy
18014  * A simple class that provides the basic implementation needed to make any element draggable.
18015  * @constructor
18016  * @param {String/HTMLElement/Element} el The container element
18017  * @param {Object} config
18018  */
18019 Roo.dd.DragSource = function(el, config){
18020     this.el = Roo.get(el);
18021     this.dragData = {};
18022     
18023     Roo.apply(this, config);
18024     
18025     if(!this.proxy){
18026         this.proxy = new Roo.dd.StatusProxy();
18027     }
18028
18029     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
18030           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
18031     
18032     this.dragging = false;
18033 };
18034
18035 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
18036     /**
18037      * @cfg {String} dropAllowed
18038      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
18039      */
18040     dropAllowed : "x-dd-drop-ok",
18041     /**
18042      * @cfg {String} dropNotAllowed
18043      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
18044      */
18045     dropNotAllowed : "x-dd-drop-nodrop",
18046
18047     /**
18048      * Returns the data object associated with this drag source
18049      * @return {Object} data An object containing arbitrary data
18050      */
18051     getDragData : function(e){
18052         return this.dragData;
18053     },
18054
18055     // private
18056     onDragEnter : function(e, id){
18057         var target = Roo.dd.DragDropMgr.getDDById(id);
18058         this.cachedTarget = target;
18059         if(this.beforeDragEnter(target, e, id) !== false){
18060             if(target.isNotifyTarget){
18061                 var status = target.notifyEnter(this, e, this.dragData);
18062                 this.proxy.setStatus(status);
18063             }else{
18064                 this.proxy.setStatus(this.dropAllowed);
18065             }
18066             
18067             if(this.afterDragEnter){
18068                 /**
18069                  * An empty function by default, but provided so that you can perform a custom action
18070                  * when the dragged item enters the drop target by providing an implementation.
18071                  * @param {Roo.dd.DragDrop} target The drop target
18072                  * @param {Event} e The event object
18073                  * @param {String} id The id of the dragged element
18074                  * @method afterDragEnter
18075                  */
18076                 this.afterDragEnter(target, e, id);
18077             }
18078         }
18079     },
18080
18081     /**
18082      * An empty function by default, but provided so that you can perform a custom action
18083      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
18084      * @param {Roo.dd.DragDrop} target The drop target
18085      * @param {Event} e The event object
18086      * @param {String} id The id of the dragged element
18087      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18088      */
18089     beforeDragEnter : function(target, e, id){
18090         return true;
18091     },
18092
18093     // private
18094     alignElWithMouse: function() {
18095         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
18096         this.proxy.sync();
18097     },
18098
18099     // private
18100     onDragOver : function(e, id){
18101         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18102         if(this.beforeDragOver(target, e, id) !== false){
18103             if(target.isNotifyTarget){
18104                 var status = target.notifyOver(this, e, this.dragData);
18105                 this.proxy.setStatus(status);
18106             }
18107
18108             if(this.afterDragOver){
18109                 /**
18110                  * An empty function by default, but provided so that you can perform a custom action
18111                  * while the dragged item is over the drop target by providing an implementation.
18112                  * @param {Roo.dd.DragDrop} target The drop target
18113                  * @param {Event} e The event object
18114                  * @param {String} id The id of the dragged element
18115                  * @method afterDragOver
18116                  */
18117                 this.afterDragOver(target, e, id);
18118             }
18119         }
18120     },
18121
18122     /**
18123      * An empty function by default, but provided so that you can perform a custom action
18124      * while the dragged item is over the drop target and optionally cancel the onDragOver.
18125      * @param {Roo.dd.DragDrop} target The drop target
18126      * @param {Event} e The event object
18127      * @param {String} id The id of the dragged element
18128      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18129      */
18130     beforeDragOver : function(target, e, id){
18131         return true;
18132     },
18133
18134     // private
18135     onDragOut : function(e, id){
18136         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18137         if(this.beforeDragOut(target, e, id) !== false){
18138             if(target.isNotifyTarget){
18139                 target.notifyOut(this, e, this.dragData);
18140             }
18141             this.proxy.reset();
18142             if(this.afterDragOut){
18143                 /**
18144                  * An empty function by default, but provided so that you can perform a custom action
18145                  * after the dragged item is dragged out of the target without dropping.
18146                  * @param {Roo.dd.DragDrop} target The drop target
18147                  * @param {Event} e The event object
18148                  * @param {String} id The id of the dragged element
18149                  * @method afterDragOut
18150                  */
18151                 this.afterDragOut(target, e, id);
18152             }
18153         }
18154         this.cachedTarget = null;
18155     },
18156
18157     /**
18158      * An empty function by default, but provided so that you can perform a custom action before the dragged
18159      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
18160      * @param {Roo.dd.DragDrop} target The drop target
18161      * @param {Event} e The event object
18162      * @param {String} id The id of the dragged element
18163      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18164      */
18165     beforeDragOut : function(target, e, id){
18166         return true;
18167     },
18168     
18169     // private
18170     onDragDrop : function(e, id){
18171         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18172         if(this.beforeDragDrop(target, e, id) !== false){
18173             if(target.isNotifyTarget){
18174                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
18175                     this.onValidDrop(target, e, id);
18176                 }else{
18177                     this.onInvalidDrop(target, e, id);
18178                 }
18179             }else{
18180                 this.onValidDrop(target, e, id);
18181             }
18182             
18183             if(this.afterDragDrop){
18184                 /**
18185                  * An empty function by default, but provided so that you can perform a custom action
18186                  * after a valid drag drop has occurred by providing an implementation.
18187                  * @param {Roo.dd.DragDrop} target The drop target
18188                  * @param {Event} e The event object
18189                  * @param {String} id The id of the dropped element
18190                  * @method afterDragDrop
18191                  */
18192                 this.afterDragDrop(target, e, id);
18193             }
18194         }
18195         delete this.cachedTarget;
18196     },
18197
18198     /**
18199      * An empty function by default, but provided so that you can perform a custom action before the dragged
18200      * item is dropped onto the target and optionally cancel the onDragDrop.
18201      * @param {Roo.dd.DragDrop} target The drop target
18202      * @param {Event} e The event object
18203      * @param {String} id The id of the dragged element
18204      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
18205      */
18206     beforeDragDrop : function(target, e, id){
18207         return true;
18208     },
18209
18210     // private
18211     onValidDrop : function(target, e, id){
18212         this.hideProxy();
18213         if(this.afterValidDrop){
18214             /**
18215              * An empty function by default, but provided so that you can perform a custom action
18216              * after a valid drop has occurred by providing an implementation.
18217              * @param {Object} target The target DD 
18218              * @param {Event} e The event object
18219              * @param {String} id The id of the dropped element
18220              * @method afterInvalidDrop
18221              */
18222             this.afterValidDrop(target, e, id);
18223         }
18224     },
18225
18226     // private
18227     getRepairXY : function(e, data){
18228         return this.el.getXY();  
18229     },
18230
18231     // private
18232     onInvalidDrop : function(target, e, id){
18233         this.beforeInvalidDrop(target, e, id);
18234         if(this.cachedTarget){
18235             if(this.cachedTarget.isNotifyTarget){
18236                 this.cachedTarget.notifyOut(this, e, this.dragData);
18237             }
18238             this.cacheTarget = null;
18239         }
18240         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
18241
18242         if(this.afterInvalidDrop){
18243             /**
18244              * An empty function by default, but provided so that you can perform a custom action
18245              * after an invalid drop has occurred by providing an implementation.
18246              * @param {Event} e The event object
18247              * @param {String} id The id of the dropped element
18248              * @method afterInvalidDrop
18249              */
18250             this.afterInvalidDrop(e, id);
18251         }
18252     },
18253
18254     // private
18255     afterRepair : function(){
18256         if(Roo.enableFx){
18257             this.el.highlight(this.hlColor || "c3daf9");
18258         }
18259         this.dragging = false;
18260     },
18261
18262     /**
18263      * An empty function by default, but provided so that you can perform a custom action after an invalid
18264      * drop has occurred.
18265      * @param {Roo.dd.DragDrop} target The drop target
18266      * @param {Event} e The event object
18267      * @param {String} id The id of the dragged element
18268      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
18269      */
18270     beforeInvalidDrop : function(target, e, id){
18271         return true;
18272     },
18273
18274     // private
18275     handleMouseDown : function(e){
18276         if(this.dragging) {
18277             return;
18278         }
18279         var data = this.getDragData(e);
18280         if(data && this.onBeforeDrag(data, e) !== false){
18281             this.dragData = data;
18282             this.proxy.stop();
18283             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
18284         } 
18285     },
18286
18287     /**
18288      * An empty function by default, but provided so that you can perform a custom action before the initial
18289      * drag event begins and optionally cancel it.
18290      * @param {Object} data An object containing arbitrary data to be shared with drop targets
18291      * @param {Event} e The event object
18292      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18293      */
18294     onBeforeDrag : function(data, e){
18295         return true;
18296     },
18297
18298     /**
18299      * An empty function by default, but provided so that you can perform a custom action once the initial
18300      * drag event has begun.  The drag cannot be canceled from this function.
18301      * @param {Number} x The x position of the click on the dragged object
18302      * @param {Number} y The y position of the click on the dragged object
18303      */
18304     onStartDrag : Roo.emptyFn,
18305
18306     // private - YUI override
18307     startDrag : function(x, y){
18308         this.proxy.reset();
18309         this.dragging = true;
18310         this.proxy.update("");
18311         this.onInitDrag(x, y);
18312         this.proxy.show();
18313     },
18314
18315     // private
18316     onInitDrag : function(x, y){
18317         var clone = this.el.dom.cloneNode(true);
18318         clone.id = Roo.id(); // prevent duplicate ids
18319         this.proxy.update(clone);
18320         this.onStartDrag(x, y);
18321         return true;
18322     },
18323
18324     /**
18325      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
18326      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
18327      */
18328     getProxy : function(){
18329         return this.proxy;  
18330     },
18331
18332     /**
18333      * Hides the drag source's {@link Roo.dd.StatusProxy}
18334      */
18335     hideProxy : function(){
18336         this.proxy.hide();  
18337         this.proxy.reset(true);
18338         this.dragging = false;
18339     },
18340
18341     // private
18342     triggerCacheRefresh : function(){
18343         Roo.dd.DDM.refreshCache(this.groups);
18344     },
18345
18346     // private - override to prevent hiding
18347     b4EndDrag: function(e) {
18348     },
18349
18350     // private - override to prevent moving
18351     endDrag : function(e){
18352         this.onEndDrag(this.dragData, e);
18353     },
18354
18355     // private
18356     onEndDrag : function(data, e){
18357     },
18358     
18359     // private - pin to cursor
18360     autoOffset : function(x, y) {
18361         this.setDelta(-12, -20);
18362     }    
18363 });/*
18364  * Based on:
18365  * Ext JS Library 1.1.1
18366  * Copyright(c) 2006-2007, Ext JS, LLC.
18367  *
18368  * Originally Released Under LGPL - original licence link has changed is not relivant.
18369  *
18370  * Fork - LGPL
18371  * <script type="text/javascript">
18372  */
18373
18374
18375 /**
18376  * @class Roo.dd.DropTarget
18377  * @extends Roo.dd.DDTarget
18378  * A simple class that provides the basic implementation needed to make any element a drop target that can have
18379  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
18380  * @constructor
18381  * @param {String/HTMLElement/Element} el The container element
18382  * @param {Object} config
18383  */
18384 Roo.dd.DropTarget = function(el, config){
18385     this.el = Roo.get(el);
18386     
18387     var listeners = false; ;
18388     if (config && config.listeners) {
18389         listeners= config.listeners;
18390         delete config.listeners;
18391     }
18392     Roo.apply(this, config);
18393     
18394     if(this.containerScroll){
18395         Roo.dd.ScrollManager.register(this.el);
18396     }
18397     this.addEvents( {
18398          /**
18399          * @scope Roo.dd.DropTarget
18400          */
18401          
18402          /**
18403          * @event enter
18404          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
18405          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
18406          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
18407          * 
18408          * IMPORTANT : it should set this.overClass and this.dropAllowed
18409          * 
18410          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18411          * @param {Event} e The event
18412          * @param {Object} data An object containing arbitrary data supplied by the drag source
18413          */
18414         "enter" : true,
18415         
18416          /**
18417          * @event over
18418          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
18419          * This method will be called on every mouse movement while the drag source is over the drop target.
18420          * This default implementation simply returns the dropAllowed config value.
18421          * 
18422          * IMPORTANT : it should set this.dropAllowed
18423          * 
18424          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18425          * @param {Event} e The event
18426          * @param {Object} data An object containing arbitrary data supplied by the drag source
18427          
18428          */
18429         "over" : true,
18430         /**
18431          * @event out
18432          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
18433          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
18434          * overClass (if any) from the drop element.
18435          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18436          * @param {Event} e The event
18437          * @param {Object} data An object containing arbitrary data supplied by the drag source
18438          */
18439          "out" : true,
18440          
18441         /**
18442          * @event drop
18443          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
18444          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
18445          * implementation that does something to process the drop event and returns true so that the drag source's
18446          * repair action does not run.
18447          * 
18448          * IMPORTANT : it should set this.success
18449          * 
18450          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18451          * @param {Event} e The event
18452          * @param {Object} data An object containing arbitrary data supplied by the drag source
18453         */
18454          "drop" : true
18455     });
18456             
18457      
18458     Roo.dd.DropTarget.superclass.constructor.call(  this, 
18459         this.el.dom, 
18460         this.ddGroup || this.group,
18461         {
18462             isTarget: true,
18463             listeners : listeners || {} 
18464            
18465         
18466         }
18467     );
18468
18469 };
18470
18471 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
18472     /**
18473      * @cfg {String} overClass
18474      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
18475      */
18476      /**
18477      * @cfg {String} ddGroup
18478      * The drag drop group to handle drop events for
18479      */
18480      
18481     /**
18482      * @cfg {String} dropAllowed
18483      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
18484      */
18485     dropAllowed : "x-dd-drop-ok",
18486     /**
18487      * @cfg {String} dropNotAllowed
18488      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
18489      */
18490     dropNotAllowed : "x-dd-drop-nodrop",
18491     /**
18492      * @cfg {boolean} success
18493      * set this after drop listener.. 
18494      */
18495     success : false,
18496     /**
18497      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
18498      * if the drop point is valid for over/enter..
18499      */
18500     valid : false,
18501     // private
18502     isTarget : true,
18503
18504     // private
18505     isNotifyTarget : true,
18506     
18507     /**
18508      * @hide
18509      */
18510     notifyEnter : function(dd, e, data)
18511     {
18512         this.valid = true;
18513         this.fireEvent('enter', dd, e, data);
18514         if(this.overClass){
18515             this.el.addClass(this.overClass);
18516         }
18517         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
18518             this.valid ? this.dropAllowed : this.dropNotAllowed
18519         );
18520     },
18521
18522     /**
18523      * @hide
18524      */
18525     notifyOver : function(dd, e, data)
18526     {
18527         this.valid = true;
18528         this.fireEvent('over', dd, e, data);
18529         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
18530             this.valid ? this.dropAllowed : this.dropNotAllowed
18531         );
18532     },
18533
18534     /**
18535      * @hide
18536      */
18537     notifyOut : function(dd, e, data)
18538     {
18539         this.fireEvent('out', dd, e, data);
18540         if(this.overClass){
18541             this.el.removeClass(this.overClass);
18542         }
18543     },
18544
18545     /**
18546      * @hide
18547      */
18548     notifyDrop : function(dd, e, data)
18549     {
18550         this.success = false;
18551         this.fireEvent('drop', dd, e, data);
18552         return this.success;
18553     }
18554 });/*
18555  * Based on:
18556  * Ext JS Library 1.1.1
18557  * Copyright(c) 2006-2007, Ext JS, LLC.
18558  *
18559  * Originally Released Under LGPL - original licence link has changed is not relivant.
18560  *
18561  * Fork - LGPL
18562  * <script type="text/javascript">
18563  */
18564
18565
18566 /**
18567  * @class Roo.dd.DragZone
18568  * @extends Roo.dd.DragSource
18569  * This class provides a container DD instance that proxies for multiple child node sources.<br />
18570  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
18571  * @constructor
18572  * @param {String/HTMLElement/Element} el The container element
18573  * @param {Object} config
18574  */
18575 Roo.dd.DragZone = function(el, config){
18576     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
18577     if(this.containerScroll){
18578         Roo.dd.ScrollManager.register(this.el);
18579     }
18580 };
18581
18582 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
18583     /**
18584      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
18585      * for auto scrolling during drag operations.
18586      */
18587     /**
18588      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
18589      * method after a failed drop (defaults to "c3daf9" - light blue)
18590      */
18591
18592     /**
18593      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
18594      * for a valid target to drag based on the mouse down. Override this method
18595      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
18596      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
18597      * @param {EventObject} e The mouse down event
18598      * @return {Object} The dragData
18599      */
18600     getDragData : function(e){
18601         return Roo.dd.Registry.getHandleFromEvent(e);
18602     },
18603     
18604     /**
18605      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
18606      * this.dragData.ddel
18607      * @param {Number} x The x position of the click on the dragged object
18608      * @param {Number} y The y position of the click on the dragged object
18609      * @return {Boolean} true to continue the drag, false to cancel
18610      */
18611     onInitDrag : function(x, y){
18612         this.proxy.update(this.dragData.ddel.cloneNode(true));
18613         this.onStartDrag(x, y);
18614         return true;
18615     },
18616     
18617     /**
18618      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
18619      */
18620     afterRepair : function(){
18621         if(Roo.enableFx){
18622             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
18623         }
18624         this.dragging = false;
18625     },
18626
18627     /**
18628      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
18629      * the XY of this.dragData.ddel
18630      * @param {EventObject} e The mouse up event
18631      * @return {Array} The xy location (e.g. [100, 200])
18632      */
18633     getRepairXY : function(e){
18634         return Roo.Element.fly(this.dragData.ddel).getXY();  
18635     }
18636 });/*
18637  * Based on:
18638  * Ext JS Library 1.1.1
18639  * Copyright(c) 2006-2007, Ext JS, LLC.
18640  *
18641  * Originally Released Under LGPL - original licence link has changed is not relivant.
18642  *
18643  * Fork - LGPL
18644  * <script type="text/javascript">
18645  */
18646 /**
18647  * @class Roo.dd.DropZone
18648  * @extends Roo.dd.DropTarget
18649  * This class provides a container DD instance that proxies for multiple child node targets.<br />
18650  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
18651  * @constructor
18652  * @param {String/HTMLElement/Element} el The container element
18653  * @param {Object} config
18654  */
18655 Roo.dd.DropZone = function(el, config){
18656     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
18657 };
18658
18659 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
18660     /**
18661      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
18662      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
18663      * provide your own custom lookup.
18664      * @param {Event} e The event
18665      * @return {Object} data The custom data
18666      */
18667     getTargetFromEvent : function(e){
18668         return Roo.dd.Registry.getTargetFromEvent(e);
18669     },
18670
18671     /**
18672      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
18673      * that it has registered.  This method has no default implementation and should be overridden to provide
18674      * node-specific processing if necessary.
18675      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
18676      * {@link #getTargetFromEvent} for this node)
18677      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18678      * @param {Event} e The event
18679      * @param {Object} data An object containing arbitrary data supplied by the drag source
18680      */
18681     onNodeEnter : function(n, dd, e, data){
18682         
18683     },
18684
18685     /**
18686      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
18687      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
18688      * overridden to provide the proper feedback.
18689      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18690      * {@link #getTargetFromEvent} for this node)
18691      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18692      * @param {Event} e The event
18693      * @param {Object} data An object containing arbitrary data supplied by the drag source
18694      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18695      * underlying {@link Roo.dd.StatusProxy} can be updated
18696      */
18697     onNodeOver : function(n, dd, e, data){
18698         return this.dropAllowed;
18699     },
18700
18701     /**
18702      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
18703      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
18704      * node-specific processing if necessary.
18705      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18706      * {@link #getTargetFromEvent} for this node)
18707      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18708      * @param {Event} e The event
18709      * @param {Object} data An object containing arbitrary data supplied by the drag source
18710      */
18711     onNodeOut : function(n, dd, e, data){
18712         
18713     },
18714
18715     /**
18716      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
18717      * the drop node.  The default implementation returns false, so it should be overridden to provide the
18718      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
18719      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18720      * {@link #getTargetFromEvent} for this node)
18721      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18722      * @param {Event} e The event
18723      * @param {Object} data An object containing arbitrary data supplied by the drag source
18724      * @return {Boolean} True if the drop was valid, else false
18725      */
18726     onNodeDrop : function(n, dd, e, data){
18727         return false;
18728     },
18729
18730     /**
18731      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
18732      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
18733      * it should be overridden to provide the proper feedback if necessary.
18734      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18735      * @param {Event} e The event
18736      * @param {Object} data An object containing arbitrary data supplied by the drag source
18737      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18738      * underlying {@link Roo.dd.StatusProxy} can be updated
18739      */
18740     onContainerOver : function(dd, e, data){
18741         return this.dropNotAllowed;
18742     },
18743
18744     /**
18745      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
18746      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
18747      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
18748      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
18749      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18750      * @param {Event} e The event
18751      * @param {Object} data An object containing arbitrary data supplied by the drag source
18752      * @return {Boolean} True if the drop was valid, else false
18753      */
18754     onContainerDrop : function(dd, e, data){
18755         return false;
18756     },
18757
18758     /**
18759      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
18760      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
18761      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
18762      * you should override this method and provide a custom implementation.
18763      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18764      * @param {Event} e The event
18765      * @param {Object} data An object containing arbitrary data supplied by the drag source
18766      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18767      * underlying {@link Roo.dd.StatusProxy} can be updated
18768      */
18769     notifyEnter : function(dd, e, data){
18770         return this.dropNotAllowed;
18771     },
18772
18773     /**
18774      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
18775      * This method will be called on every mouse movement while the drag source is over the drop zone.
18776      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
18777      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
18778      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
18779      * registered node, it will call {@link #onContainerOver}.
18780      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18781      * @param {Event} e The event
18782      * @param {Object} data An object containing arbitrary data supplied by the drag source
18783      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18784      * underlying {@link Roo.dd.StatusProxy} can be updated
18785      */
18786     notifyOver : function(dd, e, data){
18787         var n = this.getTargetFromEvent(e);
18788         if(!n){ // not over valid drop target
18789             if(this.lastOverNode){
18790                 this.onNodeOut(this.lastOverNode, dd, e, data);
18791                 this.lastOverNode = null;
18792             }
18793             return this.onContainerOver(dd, e, data);
18794         }
18795         if(this.lastOverNode != n){
18796             if(this.lastOverNode){
18797                 this.onNodeOut(this.lastOverNode, dd, e, data);
18798             }
18799             this.onNodeEnter(n, dd, e, data);
18800             this.lastOverNode = n;
18801         }
18802         return this.onNodeOver(n, dd, e, data);
18803     },
18804
18805     /**
18806      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
18807      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
18808      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
18809      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18810      * @param {Event} e The event
18811      * @param {Object} data An object containing arbitrary data supplied by the drag zone
18812      */
18813     notifyOut : function(dd, e, data){
18814         if(this.lastOverNode){
18815             this.onNodeOut(this.lastOverNode, dd, e, data);
18816             this.lastOverNode = null;
18817         }
18818     },
18819
18820     /**
18821      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
18822      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
18823      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
18824      * otherwise it will call {@link #onContainerDrop}.
18825      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18826      * @param {Event} e The event
18827      * @param {Object} data An object containing arbitrary data supplied by the drag source
18828      * @return {Boolean} True if the drop was valid, else false
18829      */
18830     notifyDrop : function(dd, e, data){
18831         if(this.lastOverNode){
18832             this.onNodeOut(this.lastOverNode, dd, e, data);
18833             this.lastOverNode = null;
18834         }
18835         var n = this.getTargetFromEvent(e);
18836         return n ?
18837             this.onNodeDrop(n, dd, e, data) :
18838             this.onContainerDrop(dd, e, data);
18839     },
18840
18841     // private
18842     triggerCacheRefresh : function(){
18843         Roo.dd.DDM.refreshCache(this.groups);
18844     }  
18845 });/*
18846  * Based on:
18847  * Ext JS Library 1.1.1
18848  * Copyright(c) 2006-2007, Ext JS, LLC.
18849  *
18850  * Originally Released Under LGPL - original licence link has changed is not relivant.
18851  *
18852  * Fork - LGPL
18853  * <script type="text/javascript">
18854  */
18855
18856
18857 /**
18858  * @class Roo.data.SortTypes
18859  * @singleton
18860  * Defines the default sorting (casting?) comparison functions used when sorting data.
18861  */
18862 Roo.data.SortTypes = {
18863     /**
18864      * Default sort that does nothing
18865      * @param {Mixed} s The value being converted
18866      * @return {Mixed} The comparison value
18867      */
18868     none : function(s){
18869         return s;
18870     },
18871     
18872     /**
18873      * The regular expression used to strip tags
18874      * @type {RegExp}
18875      * @property
18876      */
18877     stripTagsRE : /<\/?[^>]+>/gi,
18878     
18879     /**
18880      * Strips all HTML tags to sort on text only
18881      * @param {Mixed} s The value being converted
18882      * @return {String} The comparison value
18883      */
18884     asText : function(s){
18885         return String(s).replace(this.stripTagsRE, "");
18886     },
18887     
18888     /**
18889      * Strips all HTML tags to sort on text only - Case insensitive
18890      * @param {Mixed} s The value being converted
18891      * @return {String} The comparison value
18892      */
18893     asUCText : function(s){
18894         return String(s).toUpperCase().replace(this.stripTagsRE, "");
18895     },
18896     
18897     /**
18898      * Case insensitive string
18899      * @param {Mixed} s The value being converted
18900      * @return {String} The comparison value
18901      */
18902     asUCString : function(s) {
18903         return String(s).toUpperCase();
18904     },
18905     
18906     /**
18907      * Date sorting
18908      * @param {Mixed} s The value being converted
18909      * @return {Number} The comparison value
18910      */
18911     asDate : function(s) {
18912         if(!s){
18913             return 0;
18914         }
18915         if(s instanceof Date){
18916             return s.getTime();
18917         }
18918         return Date.parse(String(s));
18919     },
18920     
18921     /**
18922      * Float sorting
18923      * @param {Mixed} s The value being converted
18924      * @return {Float} The comparison value
18925      */
18926     asFloat : function(s) {
18927         var val = parseFloat(String(s).replace(/,/g, ""));
18928         if(isNaN(val)) val = 0;
18929         return val;
18930     },
18931     
18932     /**
18933      * Integer sorting
18934      * @param {Mixed} s The value being converted
18935      * @return {Number} The comparison value
18936      */
18937     asInt : function(s) {
18938         var val = parseInt(String(s).replace(/,/g, ""));
18939         if(isNaN(val)) val = 0;
18940         return val;
18941     }
18942 };/*
18943  * Based on:
18944  * Ext JS Library 1.1.1
18945  * Copyright(c) 2006-2007, Ext JS, LLC.
18946  *
18947  * Originally Released Under LGPL - original licence link has changed is not relivant.
18948  *
18949  * Fork - LGPL
18950  * <script type="text/javascript">
18951  */
18952
18953 /**
18954 * @class Roo.data.Record
18955  * Instances of this class encapsulate both record <em>definition</em> information, and record
18956  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
18957  * to access Records cached in an {@link Roo.data.Store} object.<br>
18958  * <p>
18959  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
18960  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
18961  * objects.<br>
18962  * <p>
18963  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
18964  * @constructor
18965  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
18966  * {@link #create}. The parameters are the same.
18967  * @param {Array} data An associative Array of data values keyed by the field name.
18968  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
18969  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
18970  * not specified an integer id is generated.
18971  */
18972 Roo.data.Record = function(data, id){
18973     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
18974     this.data = data;
18975 };
18976
18977 /**
18978  * Generate a constructor for a specific record layout.
18979  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
18980  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
18981  * Each field definition object may contain the following properties: <ul>
18982  * <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,
18983  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
18984  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
18985  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
18986  * is being used, then this is a string containing the javascript expression to reference the data relative to 
18987  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
18988  * to the data item relative to the record element. If the mapping expression is the same as the field name,
18989  * this may be omitted.</p></li>
18990  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
18991  * <ul><li>auto (Default, implies no conversion)</li>
18992  * <li>string</li>
18993  * <li>int</li>
18994  * <li>float</li>
18995  * <li>boolean</li>
18996  * <li>date</li></ul></p></li>
18997  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
18998  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
18999  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
19000  * by the Reader into an object that will be stored in the Record. It is passed the
19001  * following parameters:<ul>
19002  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
19003  * </ul></p></li>
19004  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
19005  * </ul>
19006  * <br>usage:<br><pre><code>
19007 var TopicRecord = Roo.data.Record.create(
19008     {name: 'title', mapping: 'topic_title'},
19009     {name: 'author', mapping: 'username'},
19010     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
19011     {name: 'lastPost', mapping: 'post_time', type: 'date'},
19012     {name: 'lastPoster', mapping: 'user2'},
19013     {name: 'excerpt', mapping: 'post_text'}
19014 );
19015
19016 var myNewRecord = new TopicRecord({
19017     title: 'Do my job please',
19018     author: 'noobie',
19019     totalPosts: 1,
19020     lastPost: new Date(),
19021     lastPoster: 'Animal',
19022     excerpt: 'No way dude!'
19023 });
19024 myStore.add(myNewRecord);
19025 </code></pre>
19026  * @method create
19027  * @static
19028  */
19029 Roo.data.Record.create = function(o){
19030     var f = function(){
19031         f.superclass.constructor.apply(this, arguments);
19032     };
19033     Roo.extend(f, Roo.data.Record);
19034     var p = f.prototype;
19035     p.fields = new Roo.util.MixedCollection(false, function(field){
19036         return field.name;
19037     });
19038     for(var i = 0, len = o.length; i < len; i++){
19039         p.fields.add(new Roo.data.Field(o[i]));
19040     }
19041     f.getField = function(name){
19042         return p.fields.get(name);  
19043     };
19044     return f;
19045 };
19046
19047 Roo.data.Record.AUTO_ID = 1000;
19048 Roo.data.Record.EDIT = 'edit';
19049 Roo.data.Record.REJECT = 'reject';
19050 Roo.data.Record.COMMIT = 'commit';
19051
19052 Roo.data.Record.prototype = {
19053     /**
19054      * Readonly flag - true if this record has been modified.
19055      * @type Boolean
19056      */
19057     dirty : false,
19058     editing : false,
19059     error: null,
19060     modified: null,
19061
19062     // private
19063     join : function(store){
19064         this.store = store;
19065     },
19066
19067     /**
19068      * Set the named field to the specified value.
19069      * @param {String} name The name of the field to set.
19070      * @param {Object} value The value to set the field to.
19071      */
19072     set : function(name, value){
19073         if(this.data[name] == value){
19074             return;
19075         }
19076         this.dirty = true;
19077         if(!this.modified){
19078             this.modified = {};
19079         }
19080         if(typeof this.modified[name] == 'undefined'){
19081             this.modified[name] = this.data[name];
19082         }
19083         this.data[name] = value;
19084         if(!this.editing && this.store){
19085             this.store.afterEdit(this);
19086         }       
19087     },
19088
19089     /**
19090      * Get the value of the named field.
19091      * @param {String} name The name of the field to get the value of.
19092      * @return {Object} The value of the field.
19093      */
19094     get : function(name){
19095         return this.data[name]; 
19096     },
19097
19098     // private
19099     beginEdit : function(){
19100         this.editing = true;
19101         this.modified = {}; 
19102     },
19103
19104     // private
19105     cancelEdit : function(){
19106         this.editing = false;
19107         delete this.modified;
19108     },
19109
19110     // private
19111     endEdit : function(){
19112         this.editing = false;
19113         if(this.dirty && this.store){
19114             this.store.afterEdit(this);
19115         }
19116     },
19117
19118     /**
19119      * Usually called by the {@link Roo.data.Store} which owns the Record.
19120      * Rejects all changes made to the Record since either creation, or the last commit operation.
19121      * Modified fields are reverted to their original values.
19122      * <p>
19123      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
19124      * of reject operations.
19125      */
19126     reject : function(){
19127         var m = this.modified;
19128         for(var n in m){
19129             if(typeof m[n] != "function"){
19130                 this.data[n] = m[n];
19131             }
19132         }
19133         this.dirty = false;
19134         delete this.modified;
19135         this.editing = false;
19136         if(this.store){
19137             this.store.afterReject(this);
19138         }
19139     },
19140
19141     /**
19142      * Usually called by the {@link Roo.data.Store} which owns the Record.
19143      * Commits all changes made to the Record since either creation, or the last commit operation.
19144      * <p>
19145      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
19146      * of commit operations.
19147      */
19148     commit : function(){
19149         this.dirty = false;
19150         delete this.modified;
19151         this.editing = false;
19152         if(this.store){
19153             this.store.afterCommit(this);
19154         }
19155     },
19156
19157     // private
19158     hasError : function(){
19159         return this.error != null;
19160     },
19161
19162     // private
19163     clearError : function(){
19164         this.error = null;
19165     },
19166
19167     /**
19168      * Creates a copy of this record.
19169      * @param {String} id (optional) A new record id if you don't want to use this record's id
19170      * @return {Record}
19171      */
19172     copy : function(newId) {
19173         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
19174     }
19175 };/*
19176  * Based on:
19177  * Ext JS Library 1.1.1
19178  * Copyright(c) 2006-2007, Ext JS, LLC.
19179  *
19180  * Originally Released Under LGPL - original licence link has changed is not relivant.
19181  *
19182  * Fork - LGPL
19183  * <script type="text/javascript">
19184  */
19185
19186
19187
19188 /**
19189  * @class Roo.data.Store
19190  * @extends Roo.util.Observable
19191  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
19192  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
19193  * <p>
19194  * 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
19195  * has no knowledge of the format of the data returned by the Proxy.<br>
19196  * <p>
19197  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
19198  * instances from the data object. These records are cached and made available through accessor functions.
19199  * @constructor
19200  * Creates a new Store.
19201  * @param {Object} config A config object containing the objects needed for the Store to access data,
19202  * and read the data into Records.
19203  */
19204 Roo.data.Store = function(config){
19205     this.data = new Roo.util.MixedCollection(false);
19206     this.data.getKey = function(o){
19207         return o.id;
19208     };
19209     this.baseParams = {};
19210     // private
19211     this.paramNames = {
19212         "start" : "start",
19213         "limit" : "limit",
19214         "sort" : "sort",
19215         "dir" : "dir",
19216         "multisort" : "_multisort"
19217     };
19218
19219     if(config && config.data){
19220         this.inlineData = config.data;
19221         delete config.data;
19222     }
19223
19224     Roo.apply(this, config);
19225     
19226     if(this.reader){ // reader passed
19227         this.reader = Roo.factory(this.reader, Roo.data);
19228         this.reader.xmodule = this.xmodule || false;
19229         if(!this.recordType){
19230             this.recordType = this.reader.recordType;
19231         }
19232         if(this.reader.onMetaChange){
19233             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
19234         }
19235     }
19236
19237     if(this.recordType){
19238         this.fields = this.recordType.prototype.fields;
19239     }
19240     this.modified = [];
19241
19242     this.addEvents({
19243         /**
19244          * @event datachanged
19245          * Fires when the data cache has changed, and a widget which is using this Store
19246          * as a Record cache should refresh its view.
19247          * @param {Store} this
19248          */
19249         datachanged : true,
19250         /**
19251          * @event metachange
19252          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
19253          * @param {Store} this
19254          * @param {Object} meta The JSON metadata
19255          */
19256         metachange : true,
19257         /**
19258          * @event add
19259          * Fires when Records have been added to the Store
19260          * @param {Store} this
19261          * @param {Roo.data.Record[]} records The array of Records added
19262          * @param {Number} index The index at which the record(s) were added
19263          */
19264         add : true,
19265         /**
19266          * @event remove
19267          * Fires when a Record has been removed from the Store
19268          * @param {Store} this
19269          * @param {Roo.data.Record} record The Record that was removed
19270          * @param {Number} index The index at which the record was removed
19271          */
19272         remove : true,
19273         /**
19274          * @event update
19275          * Fires when a Record has been updated
19276          * @param {Store} this
19277          * @param {Roo.data.Record} record The Record that was updated
19278          * @param {String} operation The update operation being performed.  Value may be one of:
19279          * <pre><code>
19280  Roo.data.Record.EDIT
19281  Roo.data.Record.REJECT
19282  Roo.data.Record.COMMIT
19283          * </code></pre>
19284          */
19285         update : true,
19286         /**
19287          * @event clear
19288          * Fires when the data cache has been cleared.
19289          * @param {Store} this
19290          */
19291         clear : true,
19292         /**
19293          * @event beforeload
19294          * Fires before a request is made for a new data object.  If the beforeload handler returns false
19295          * the load action will be canceled.
19296          * @param {Store} this
19297          * @param {Object} options The loading options that were specified (see {@link #load} for details)
19298          */
19299         beforeload : true,
19300         /**
19301          * @event load
19302          * Fires after a new set of Records has been loaded.
19303          * @param {Store} this
19304          * @param {Roo.data.Record[]} records The Records that were loaded
19305          * @param {Object} options The loading options that were specified (see {@link #load} for details)
19306          */
19307         load : true,
19308         /**
19309          * @event loadexception
19310          * Fires if an exception occurs in the Proxy during loading.
19311          * Called with the signature of the Proxy's "loadexception" event.
19312          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
19313          * 
19314          * @param {Proxy} 
19315          * @param {Object} return from JsonData.reader() - success, totalRecords, records
19316          * @param {Object} load options 
19317          * @param {Object} jsonData from your request (normally this contains the Exception)
19318          */
19319         loadexception : true
19320     });
19321     
19322     if(this.proxy){
19323         this.proxy = Roo.factory(this.proxy, Roo.data);
19324         this.proxy.xmodule = this.xmodule || false;
19325         this.relayEvents(this.proxy,  ["loadexception"]);
19326     }
19327     this.sortToggle = {};
19328     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
19329
19330     Roo.data.Store.superclass.constructor.call(this);
19331
19332     if(this.inlineData){
19333         this.loadData(this.inlineData);
19334         delete this.inlineData;
19335     }
19336 };
19337 Roo.extend(Roo.data.Store, Roo.util.Observable, {
19338      /**
19339     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
19340     * without a remote query - used by combo/forms at present.
19341     */
19342     
19343     /**
19344     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
19345     */
19346     /**
19347     * @cfg {Array} data Inline data to be loaded when the store is initialized.
19348     */
19349     /**
19350     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
19351     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
19352     */
19353     /**
19354     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
19355     * on any HTTP request
19356     */
19357     /**
19358     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
19359     */
19360     /**
19361     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
19362     */
19363     multiSort: false,
19364     /**
19365     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
19366     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
19367     */
19368     remoteSort : false,
19369
19370     /**
19371     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
19372      * loaded or when a record is removed. (defaults to false).
19373     */
19374     pruneModifiedRecords : false,
19375
19376     // private
19377     lastOptions : null,
19378
19379     /**
19380      * Add Records to the Store and fires the add event.
19381      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19382      */
19383     add : function(records){
19384         records = [].concat(records);
19385         for(var i = 0, len = records.length; i < len; i++){
19386             records[i].join(this);
19387         }
19388         var index = this.data.length;
19389         this.data.addAll(records);
19390         this.fireEvent("add", this, records, index);
19391     },
19392
19393     /**
19394      * Remove a Record from the Store and fires the remove event.
19395      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
19396      */
19397     remove : function(record){
19398         var index = this.data.indexOf(record);
19399         this.data.removeAt(index);
19400         if(this.pruneModifiedRecords){
19401             this.modified.remove(record);
19402         }
19403         this.fireEvent("remove", this, record, index);
19404     },
19405
19406     /**
19407      * Remove all Records from the Store and fires the clear event.
19408      */
19409     removeAll : function(){
19410         this.data.clear();
19411         if(this.pruneModifiedRecords){
19412             this.modified = [];
19413         }
19414         this.fireEvent("clear", this);
19415     },
19416
19417     /**
19418      * Inserts Records to the Store at the given index and fires the add event.
19419      * @param {Number} index The start index at which to insert the passed Records.
19420      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19421      */
19422     insert : function(index, records){
19423         records = [].concat(records);
19424         for(var i = 0, len = records.length; i < len; i++){
19425             this.data.insert(index, records[i]);
19426             records[i].join(this);
19427         }
19428         this.fireEvent("add", this, records, index);
19429     },
19430
19431     /**
19432      * Get the index within the cache of the passed Record.
19433      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
19434      * @return {Number} The index of the passed Record. Returns -1 if not found.
19435      */
19436     indexOf : function(record){
19437         return this.data.indexOf(record);
19438     },
19439
19440     /**
19441      * Get the index within the cache of the Record with the passed id.
19442      * @param {String} id The id of the Record to find.
19443      * @return {Number} The index of the Record. Returns -1 if not found.
19444      */
19445     indexOfId : function(id){
19446         return this.data.indexOfKey(id);
19447     },
19448
19449     /**
19450      * Get the Record with the specified id.
19451      * @param {String} id The id of the Record to find.
19452      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
19453      */
19454     getById : function(id){
19455         return this.data.key(id);
19456     },
19457
19458     /**
19459      * Get the Record at the specified index.
19460      * @param {Number} index The index of the Record to find.
19461      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
19462      */
19463     getAt : function(index){
19464         return this.data.itemAt(index);
19465     },
19466
19467     /**
19468      * Returns a range of Records between specified indices.
19469      * @param {Number} startIndex (optional) The starting index (defaults to 0)
19470      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
19471      * @return {Roo.data.Record[]} An array of Records
19472      */
19473     getRange : function(start, end){
19474         return this.data.getRange(start, end);
19475     },
19476
19477     // private
19478     storeOptions : function(o){
19479         o = Roo.apply({}, o);
19480         delete o.callback;
19481         delete o.scope;
19482         this.lastOptions = o;
19483     },
19484
19485     /**
19486      * Loads the Record cache from the configured Proxy using the configured Reader.
19487      * <p>
19488      * If using remote paging, then the first load call must specify the <em>start</em>
19489      * and <em>limit</em> properties in the options.params property to establish the initial
19490      * position within the dataset, and the number of Records to cache on each read from the Proxy.
19491      * <p>
19492      * <strong>It is important to note that for remote data sources, loading is asynchronous,
19493      * and this call will return before the new data has been loaded. Perform any post-processing
19494      * in a callback function, or in a "load" event handler.</strong>
19495      * <p>
19496      * @param {Object} options An object containing properties which control loading options:<ul>
19497      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
19498      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
19499      * passed the following arguments:<ul>
19500      * <li>r : Roo.data.Record[]</li>
19501      * <li>options: Options object from the load call</li>
19502      * <li>success: Boolean success indicator</li></ul></li>
19503      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
19504      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
19505      * </ul>
19506      */
19507     load : function(options){
19508         options = options || {};
19509         if(this.fireEvent("beforeload", this, options) !== false){
19510             this.storeOptions(options);
19511             var p = Roo.apply(options.params || {}, this.baseParams);
19512             // if meta was not loaded from remote source.. try requesting it.
19513             if (!this.reader.metaFromRemote) {
19514                 p._requestMeta = 1;
19515             }
19516             if(this.sortInfo && this.remoteSort){
19517                 var pn = this.paramNames;
19518                 p[pn["sort"]] = this.sortInfo.field;
19519                 p[pn["dir"]] = this.sortInfo.direction;
19520             }
19521             if (this.multiSort) {
19522                 var pn = this.paramNames;
19523                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
19524             }
19525             
19526             this.proxy.load(p, this.reader, this.loadRecords, this, options);
19527         }
19528     },
19529
19530     /**
19531      * Reloads the Record cache from the configured Proxy using the configured Reader and
19532      * the options from the last load operation performed.
19533      * @param {Object} options (optional) An object containing properties which may override the options
19534      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
19535      * the most recently used options are reused).
19536      */
19537     reload : function(options){
19538         this.load(Roo.applyIf(options||{}, this.lastOptions));
19539     },
19540
19541     // private
19542     // Called as a callback by the Reader during a load operation.
19543     loadRecords : function(o, options, success){
19544         if(!o || success === false){
19545             if(success !== false){
19546                 this.fireEvent("load", this, [], options);
19547             }
19548             if(options.callback){
19549                 options.callback.call(options.scope || this, [], options, false);
19550             }
19551             return;
19552         }
19553         // if data returned failure - throw an exception.
19554         if (o.success === false) {
19555             // show a message if no listener is registered.
19556             if (!this.hasListener('loadexception') && typeof(this.reader.jsonData.errorMsg) != 'undefined') {
19557                     Roo.MessageBox.alert("Error loading",this.reader.jsonData.errorMsg);
19558             }
19559             // loadmask wil be hooked into this..
19560             this.fireEvent("loadexception", this, o, options, this.reader.jsonData);
19561             return;
19562         }
19563         var r = o.records, t = o.totalRecords || r.length;
19564         if(!options || options.add !== true){
19565             if(this.pruneModifiedRecords){
19566                 this.modified = [];
19567             }
19568             for(var i = 0, len = r.length; i < len; i++){
19569                 r[i].join(this);
19570             }
19571             if(this.snapshot){
19572                 this.data = this.snapshot;
19573                 delete this.snapshot;
19574             }
19575             this.data.clear();
19576             this.data.addAll(r);
19577             this.totalLength = t;
19578             this.applySort();
19579             this.fireEvent("datachanged", this);
19580         }else{
19581             this.totalLength = Math.max(t, this.data.length+r.length);
19582             this.add(r);
19583         }
19584         this.fireEvent("load", this, r, options);
19585         if(options.callback){
19586             options.callback.call(options.scope || this, r, options, true);
19587         }
19588     },
19589
19590
19591     /**
19592      * Loads data from a passed data block. A Reader which understands the format of the data
19593      * must have been configured in the constructor.
19594      * @param {Object} data The data block from which to read the Records.  The format of the data expected
19595      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
19596      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
19597      */
19598     loadData : function(o, append){
19599         var r = this.reader.readRecords(o);
19600         this.loadRecords(r, {add: append}, true);
19601     },
19602
19603     /**
19604      * Gets the number of cached records.
19605      * <p>
19606      * <em>If using paging, this may not be the total size of the dataset. If the data object
19607      * used by the Reader contains the dataset size, then the getTotalCount() function returns
19608      * the data set size</em>
19609      */
19610     getCount : function(){
19611         return this.data.length || 0;
19612     },
19613
19614     /**
19615      * Gets the total number of records in the dataset as returned by the server.
19616      * <p>
19617      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
19618      * the dataset size</em>
19619      */
19620     getTotalCount : function(){
19621         return this.totalLength || 0;
19622     },
19623
19624     /**
19625      * Returns the sort state of the Store as an object with two properties:
19626      * <pre><code>
19627  field {String} The name of the field by which the Records are sorted
19628  direction {String} The sort order, "ASC" or "DESC"
19629      * </code></pre>
19630      */
19631     getSortState : function(){
19632         return this.sortInfo;
19633     },
19634
19635     // private
19636     applySort : function(){
19637         if(this.sortInfo && !this.remoteSort){
19638             var s = this.sortInfo, f = s.field;
19639             var st = this.fields.get(f).sortType;
19640             var fn = function(r1, r2){
19641                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
19642                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
19643             };
19644             this.data.sort(s.direction, fn);
19645             if(this.snapshot && this.snapshot != this.data){
19646                 this.snapshot.sort(s.direction, fn);
19647             }
19648         }
19649     },
19650
19651     /**
19652      * Sets the default sort column and order to be used by the next load operation.
19653      * @param {String} fieldName The name of the field to sort by.
19654      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
19655      */
19656     setDefaultSort : function(field, dir){
19657         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
19658     },
19659
19660     /**
19661      * Sort the Records.
19662      * If remote sorting is used, the sort is performed on the server, and the cache is
19663      * reloaded. If local sorting is used, the cache is sorted internally.
19664      * @param {String} fieldName The name of the field to sort by.
19665      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
19666      */
19667     sort : function(fieldName, dir){
19668         var f = this.fields.get(fieldName);
19669         if(!dir){
19670             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
19671             
19672             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
19673                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
19674             }else{
19675                 dir = f.sortDir;
19676             }
19677         }
19678         this.sortToggle[f.name] = dir;
19679         this.sortInfo = {field: f.name, direction: dir};
19680         if(!this.remoteSort){
19681             this.applySort();
19682             this.fireEvent("datachanged", this);
19683         }else{
19684             this.load(this.lastOptions);
19685         }
19686     },
19687
19688     /**
19689      * Calls the specified function for each of the Records in the cache.
19690      * @param {Function} fn The function to call. The Record is passed as the first parameter.
19691      * Returning <em>false</em> aborts and exits the iteration.
19692      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
19693      */
19694     each : function(fn, scope){
19695         this.data.each(fn, scope);
19696     },
19697
19698     /**
19699      * Gets all records modified since the last commit.  Modified records are persisted across load operations
19700      * (e.g., during paging).
19701      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
19702      */
19703     getModifiedRecords : function(){
19704         return this.modified;
19705     },
19706
19707     // private
19708     createFilterFn : function(property, value, anyMatch){
19709         if(!value.exec){ // not a regex
19710             value = String(value);
19711             if(value.length == 0){
19712                 return false;
19713             }
19714             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
19715         }
19716         return function(r){
19717             return value.test(r.data[property]);
19718         };
19719     },
19720
19721     /**
19722      * Sums the value of <i>property</i> for each record between start and end and returns the result.
19723      * @param {String} property A field on your records
19724      * @param {Number} start The record index to start at (defaults to 0)
19725      * @param {Number} end The last record index to include (defaults to length - 1)
19726      * @return {Number} The sum
19727      */
19728     sum : function(property, start, end){
19729         var rs = this.data.items, v = 0;
19730         start = start || 0;
19731         end = (end || end === 0) ? end : rs.length-1;
19732
19733         for(var i = start; i <= end; i++){
19734             v += (rs[i].data[property] || 0);
19735         }
19736         return v;
19737     },
19738
19739     /**
19740      * Filter the records by a specified property.
19741      * @param {String} field A field on your records
19742      * @param {String/RegExp} value Either a string that the field
19743      * should start with or a RegExp to test against the field
19744      * @param {Boolean} anyMatch True to match any part not just the beginning
19745      */
19746     filter : function(property, value, anyMatch){
19747         var fn = this.createFilterFn(property, value, anyMatch);
19748         return fn ? this.filterBy(fn) : this.clearFilter();
19749     },
19750
19751     /**
19752      * Filter by a function. The specified function will be called with each
19753      * record in this data source. If the function returns true the record is included,
19754      * otherwise it is filtered.
19755      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
19756      * @param {Object} scope (optional) The scope of the function (defaults to this)
19757      */
19758     filterBy : function(fn, scope){
19759         this.snapshot = this.snapshot || this.data;
19760         this.data = this.queryBy(fn, scope||this);
19761         this.fireEvent("datachanged", this);
19762     },
19763
19764     /**
19765      * Query the records by a specified property.
19766      * @param {String} field A field on your records
19767      * @param {String/RegExp} value Either a string that the field
19768      * should start with or a RegExp to test against the field
19769      * @param {Boolean} anyMatch True to match any part not just the beginning
19770      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
19771      */
19772     query : function(property, value, anyMatch){
19773         var fn = this.createFilterFn(property, value, anyMatch);
19774         return fn ? this.queryBy(fn) : this.data.clone();
19775     },
19776
19777     /**
19778      * Query by a function. The specified function will be called with each
19779      * record in this data source. If the function returns true the record is included
19780      * in the results.
19781      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
19782      * @param {Object} scope (optional) The scope of the function (defaults to this)
19783       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
19784      **/
19785     queryBy : function(fn, scope){
19786         var data = this.snapshot || this.data;
19787         return data.filterBy(fn, scope||this);
19788     },
19789
19790     /**
19791      * Collects unique values for a particular dataIndex from this store.
19792      * @param {String} dataIndex The property to collect
19793      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
19794      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
19795      * @return {Array} An array of the unique values
19796      **/
19797     collect : function(dataIndex, allowNull, bypassFilter){
19798         var d = (bypassFilter === true && this.snapshot) ?
19799                 this.snapshot.items : this.data.items;
19800         var v, sv, r = [], l = {};
19801         for(var i = 0, len = d.length; i < len; i++){
19802             v = d[i].data[dataIndex];
19803             sv = String(v);
19804             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
19805                 l[sv] = true;
19806                 r[r.length] = v;
19807             }
19808         }
19809         return r;
19810     },
19811
19812     /**
19813      * Revert to a view of the Record cache with no filtering applied.
19814      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
19815      */
19816     clearFilter : function(suppressEvent){
19817         if(this.snapshot && this.snapshot != this.data){
19818             this.data = this.snapshot;
19819             delete this.snapshot;
19820             if(suppressEvent !== true){
19821                 this.fireEvent("datachanged", this);
19822             }
19823         }
19824     },
19825
19826     // private
19827     afterEdit : function(record){
19828         if(this.modified.indexOf(record) == -1){
19829             this.modified.push(record);
19830         }
19831         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
19832     },
19833     
19834     // private
19835     afterReject : function(record){
19836         this.modified.remove(record);
19837         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
19838     },
19839
19840     // private
19841     afterCommit : function(record){
19842         this.modified.remove(record);
19843         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
19844     },
19845
19846     /**
19847      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
19848      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
19849      */
19850     commitChanges : function(){
19851         var m = this.modified.slice(0);
19852         this.modified = [];
19853         for(var i = 0, len = m.length; i < len; i++){
19854             m[i].commit();
19855         }
19856     },
19857
19858     /**
19859      * Cancel outstanding changes on all changed records.
19860      */
19861     rejectChanges : function(){
19862         var m = this.modified.slice(0);
19863         this.modified = [];
19864         for(var i = 0, len = m.length; i < len; i++){
19865             m[i].reject();
19866         }
19867     },
19868
19869     onMetaChange : function(meta, rtype, o){
19870         this.recordType = rtype;
19871         this.fields = rtype.prototype.fields;
19872         delete this.snapshot;
19873         this.sortInfo = meta.sortInfo || this.sortInfo;
19874         this.modified = [];
19875         this.fireEvent('metachange', this, this.reader.meta);
19876     }
19877 });/*
19878  * Based on:
19879  * Ext JS Library 1.1.1
19880  * Copyright(c) 2006-2007, Ext JS, LLC.
19881  *
19882  * Originally Released Under LGPL - original licence link has changed is not relivant.
19883  *
19884  * Fork - LGPL
19885  * <script type="text/javascript">
19886  */
19887
19888 /**
19889  * @class Roo.data.SimpleStore
19890  * @extends Roo.data.Store
19891  * Small helper class to make creating Stores from Array data easier.
19892  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
19893  * @cfg {Array} fields An array of field definition objects, or field name strings.
19894  * @cfg {Array} data The multi-dimensional array of data
19895  * @constructor
19896  * @param {Object} config
19897  */
19898 Roo.data.SimpleStore = function(config){
19899     Roo.data.SimpleStore.superclass.constructor.call(this, {
19900         isLocal : true,
19901         reader: new Roo.data.ArrayReader({
19902                 id: config.id
19903             },
19904             Roo.data.Record.create(config.fields)
19905         ),
19906         proxy : new Roo.data.MemoryProxy(config.data)
19907     });
19908     this.load();
19909 };
19910 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
19911  * Based on:
19912  * Ext JS Library 1.1.1
19913  * Copyright(c) 2006-2007, Ext JS, LLC.
19914  *
19915  * Originally Released Under LGPL - original licence link has changed is not relivant.
19916  *
19917  * Fork - LGPL
19918  * <script type="text/javascript">
19919  */
19920
19921 /**
19922 /**
19923  * @extends Roo.data.Store
19924  * @class Roo.data.JsonStore
19925  * Small helper class to make creating Stores for JSON data easier. <br/>
19926 <pre><code>
19927 var store = new Roo.data.JsonStore({
19928     url: 'get-images.php',
19929     root: 'images',
19930     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
19931 });
19932 </code></pre>
19933  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
19934  * JsonReader and HttpProxy (unless inline data is provided).</b>
19935  * @cfg {Array} fields An array of field definition objects, or field name strings.
19936  * @constructor
19937  * @param {Object} config
19938  */
19939 Roo.data.JsonStore = function(c){
19940     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
19941         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
19942         reader: new Roo.data.JsonReader(c, c.fields)
19943     }));
19944 };
19945 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
19946  * Based on:
19947  * Ext JS Library 1.1.1
19948  * Copyright(c) 2006-2007, Ext JS, LLC.
19949  *
19950  * Originally Released Under LGPL - original licence link has changed is not relivant.
19951  *
19952  * Fork - LGPL
19953  * <script type="text/javascript">
19954  */
19955
19956  
19957 Roo.data.Field = function(config){
19958     if(typeof config == "string"){
19959         config = {name: config};
19960     }
19961     Roo.apply(this, config);
19962     
19963     if(!this.type){
19964         this.type = "auto";
19965     }
19966     
19967     var st = Roo.data.SortTypes;
19968     // named sortTypes are supported, here we look them up
19969     if(typeof this.sortType == "string"){
19970         this.sortType = st[this.sortType];
19971     }
19972     
19973     // set default sortType for strings and dates
19974     if(!this.sortType){
19975         switch(this.type){
19976             case "string":
19977                 this.sortType = st.asUCString;
19978                 break;
19979             case "date":
19980                 this.sortType = st.asDate;
19981                 break;
19982             default:
19983                 this.sortType = st.none;
19984         }
19985     }
19986
19987     // define once
19988     var stripRe = /[\$,%]/g;
19989
19990     // prebuilt conversion function for this field, instead of
19991     // switching every time we're reading a value
19992     if(!this.convert){
19993         var cv, dateFormat = this.dateFormat;
19994         switch(this.type){
19995             case "":
19996             case "auto":
19997             case undefined:
19998                 cv = function(v){ return v; };
19999                 break;
20000             case "string":
20001                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
20002                 break;
20003             case "int":
20004                 cv = function(v){
20005                     return v !== undefined && v !== null && v !== '' ?
20006                            parseInt(String(v).replace(stripRe, ""), 10) : '';
20007                     };
20008                 break;
20009             case "float":
20010                 cv = function(v){
20011                     return v !== undefined && v !== null && v !== '' ?
20012                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
20013                     };
20014                 break;
20015             case "bool":
20016             case "boolean":
20017                 cv = function(v){ return v === true || v === "true" || v == 1; };
20018                 break;
20019             case "date":
20020                 cv = function(v){
20021                     if(!v){
20022                         return '';
20023                     }
20024                     if(v instanceof Date){
20025                         return v;
20026                     }
20027                     if(dateFormat){
20028                         if(dateFormat == "timestamp"){
20029                             return new Date(v*1000);
20030                         }
20031                         return Date.parseDate(v, dateFormat);
20032                     }
20033                     var parsed = Date.parse(v);
20034                     return parsed ? new Date(parsed) : null;
20035                 };
20036              break;
20037             
20038         }
20039         this.convert = cv;
20040     }
20041 };
20042
20043 Roo.data.Field.prototype = {
20044     dateFormat: null,
20045     defaultValue: "",
20046     mapping: null,
20047     sortType : null,
20048     sortDir : "ASC"
20049 };/*
20050  * Based on:
20051  * Ext JS Library 1.1.1
20052  * Copyright(c) 2006-2007, Ext JS, LLC.
20053  *
20054  * Originally Released Under LGPL - original licence link has changed is not relivant.
20055  *
20056  * Fork - LGPL
20057  * <script type="text/javascript">
20058  */
20059  
20060 // Base class for reading structured data from a data source.  This class is intended to be
20061 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
20062
20063 /**
20064  * @class Roo.data.DataReader
20065  * Base class for reading structured data from a data source.  This class is intended to be
20066  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
20067  */
20068
20069 Roo.data.DataReader = function(meta, recordType){
20070     
20071     this.meta = meta;
20072     
20073     this.recordType = recordType instanceof Array ? 
20074         Roo.data.Record.create(recordType) : recordType;
20075 };
20076
20077 Roo.data.DataReader.prototype = {
20078      /**
20079      * Create an empty record
20080      * @param {Object} data (optional) - overlay some values
20081      * @return {Roo.data.Record} record created.
20082      */
20083     newRow :  function(d) {
20084         var da =  {};
20085         this.recordType.prototype.fields.each(function(c) {
20086             switch( c.type) {
20087                 case 'int' : da[c.name] = 0; break;
20088                 case 'date' : da[c.name] = new Date(); break;
20089                 case 'float' : da[c.name] = 0.0; break;
20090                 case 'boolean' : da[c.name] = false; break;
20091                 default : da[c.name] = ""; break;
20092             }
20093             
20094         });
20095         return new this.recordType(Roo.apply(da, d));
20096     }
20097     
20098 };/*
20099  * Based on:
20100  * Ext JS Library 1.1.1
20101  * Copyright(c) 2006-2007, Ext JS, LLC.
20102  *
20103  * Originally Released Under LGPL - original licence link has changed is not relivant.
20104  *
20105  * Fork - LGPL
20106  * <script type="text/javascript">
20107  */
20108
20109 /**
20110  * @class Roo.data.DataProxy
20111  * @extends Roo.data.Observable
20112  * This class is an abstract base class for implementations which provide retrieval of
20113  * unformatted data objects.<br>
20114  * <p>
20115  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
20116  * (of the appropriate type which knows how to parse the data object) to provide a block of
20117  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
20118  * <p>
20119  * Custom implementations must implement the load method as described in
20120  * {@link Roo.data.HttpProxy#load}.
20121  */
20122 Roo.data.DataProxy = function(){
20123     this.addEvents({
20124         /**
20125          * @event beforeload
20126          * Fires before a network request is made to retrieve a data object.
20127          * @param {Object} This DataProxy object.
20128          * @param {Object} params The params parameter to the load function.
20129          */
20130         beforeload : true,
20131         /**
20132          * @event load
20133          * Fires before the load method's callback is called.
20134          * @param {Object} This DataProxy object.
20135          * @param {Object} o The data object.
20136          * @param {Object} arg The callback argument object passed to the load function.
20137          */
20138         load : true,
20139         /**
20140          * @event loadexception
20141          * Fires if an Exception occurs during data retrieval.
20142          * @param {Object} This DataProxy object.
20143          * @param {Object} o The data object.
20144          * @param {Object} arg The callback argument object passed to the load function.
20145          * @param {Object} e The Exception.
20146          */
20147         loadexception : true
20148     });
20149     Roo.data.DataProxy.superclass.constructor.call(this);
20150 };
20151
20152 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
20153
20154     /**
20155      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
20156      */
20157 /*
20158  * Based on:
20159  * Ext JS Library 1.1.1
20160  * Copyright(c) 2006-2007, Ext JS, LLC.
20161  *
20162  * Originally Released Under LGPL - original licence link has changed is not relivant.
20163  *
20164  * Fork - LGPL
20165  * <script type="text/javascript">
20166  */
20167 /**
20168  * @class Roo.data.MemoryProxy
20169  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
20170  * to the Reader when its load method is called.
20171  * @constructor
20172  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
20173  */
20174 Roo.data.MemoryProxy = function(data){
20175     if (data.data) {
20176         data = data.data;
20177     }
20178     Roo.data.MemoryProxy.superclass.constructor.call(this);
20179     this.data = data;
20180 };
20181
20182 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
20183     /**
20184      * Load data from the requested source (in this case an in-memory
20185      * data object passed to the constructor), read the data object into
20186      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
20187      * process that block using the passed callback.
20188      * @param {Object} params This parameter is not used by the MemoryProxy class.
20189      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20190      * object into a block of Roo.data.Records.
20191      * @param {Function} callback The function into which to pass the block of Roo.data.records.
20192      * The function must be passed <ul>
20193      * <li>The Record block object</li>
20194      * <li>The "arg" argument from the load function</li>
20195      * <li>A boolean success indicator</li>
20196      * </ul>
20197      * @param {Object} scope The scope in which to call the callback
20198      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20199      */
20200     load : function(params, reader, callback, scope, arg){
20201         params = params || {};
20202         var result;
20203         try {
20204             result = reader.readRecords(this.data);
20205         }catch(e){
20206             this.fireEvent("loadexception", this, arg, null, e);
20207             callback.call(scope, null, arg, false);
20208             return;
20209         }
20210         callback.call(scope, result, arg, true);
20211     },
20212     
20213     // private
20214     update : function(params, records){
20215         
20216     }
20217 });/*
20218  * Based on:
20219  * Ext JS Library 1.1.1
20220  * Copyright(c) 2006-2007, Ext JS, LLC.
20221  *
20222  * Originally Released Under LGPL - original licence link has changed is not relivant.
20223  *
20224  * Fork - LGPL
20225  * <script type="text/javascript">
20226  */
20227 /**
20228  * @class Roo.data.HttpProxy
20229  * @extends Roo.data.DataProxy
20230  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
20231  * configured to reference a certain URL.<br><br>
20232  * <p>
20233  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
20234  * from which the running page was served.<br><br>
20235  * <p>
20236  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
20237  * <p>
20238  * Be aware that to enable the browser to parse an XML document, the server must set
20239  * the Content-Type header in the HTTP response to "text/xml".
20240  * @constructor
20241  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
20242  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
20243  * will be used to make the request.
20244  */
20245 Roo.data.HttpProxy = function(conn){
20246     Roo.data.HttpProxy.superclass.constructor.call(this);
20247     // is conn a conn config or a real conn?
20248     this.conn = conn;
20249     this.useAjax = !conn || !conn.events;
20250   
20251 };
20252
20253 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
20254     // thse are take from connection...
20255     
20256     /**
20257      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
20258      */
20259     /**
20260      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
20261      * extra parameters to each request made by this object. (defaults to undefined)
20262      */
20263     /**
20264      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
20265      *  to each request made by this object. (defaults to undefined)
20266      */
20267     /**
20268      * @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)
20269      */
20270     /**
20271      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
20272      */
20273      /**
20274      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
20275      * @type Boolean
20276      */
20277   
20278
20279     /**
20280      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
20281      * @type Boolean
20282      */
20283     /**
20284      * Return the {@link Roo.data.Connection} object being used by this Proxy.
20285      * @return {Connection} The Connection object. This object may be used to subscribe to events on
20286      * a finer-grained basis than the DataProxy events.
20287      */
20288     getConnection : function(){
20289         return this.useAjax ? Roo.Ajax : this.conn;
20290     },
20291
20292     /**
20293      * Load data from the configured {@link Roo.data.Connection}, read the data object into
20294      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
20295      * process that block using the passed callback.
20296      * @param {Object} params An object containing properties which are to be used as HTTP parameters
20297      * for the request to the remote server.
20298      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20299      * object into a block of Roo.data.Records.
20300      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
20301      * The function must be passed <ul>
20302      * <li>The Record block object</li>
20303      * <li>The "arg" argument from the load function</li>
20304      * <li>A boolean success indicator</li>
20305      * </ul>
20306      * @param {Object} scope The scope in which to call the callback
20307      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20308      */
20309     load : function(params, reader, callback, scope, arg){
20310         if(this.fireEvent("beforeload", this, params) !== false){
20311             var  o = {
20312                 params : params || {},
20313                 request: {
20314                     callback : callback,
20315                     scope : scope,
20316                     arg : arg
20317                 },
20318                 reader: reader,
20319                 callback : this.loadResponse,
20320                 scope: this
20321             };
20322             if(this.useAjax){
20323                 Roo.applyIf(o, this.conn);
20324                 if(this.activeRequest){
20325                     Roo.Ajax.abort(this.activeRequest);
20326                 }
20327                 this.activeRequest = Roo.Ajax.request(o);
20328             }else{
20329                 this.conn.request(o);
20330             }
20331         }else{
20332             callback.call(scope||this, null, arg, false);
20333         }
20334     },
20335
20336     // private
20337     loadResponse : function(o, success, response){
20338         delete this.activeRequest;
20339         if(!success){
20340             this.fireEvent("loadexception", this, o, response);
20341             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20342             return;
20343         }
20344         var result;
20345         try {
20346             result = o.reader.read(response);
20347         }catch(e){
20348             this.fireEvent("loadexception", this, o, response, e);
20349             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20350             return;
20351         }
20352         
20353         this.fireEvent("load", this, o, o.request.arg);
20354         o.request.callback.call(o.request.scope, result, o.request.arg, true);
20355     },
20356
20357     // private
20358     update : function(dataSet){
20359
20360     },
20361
20362     // private
20363     updateResponse : function(dataSet){
20364
20365     }
20366 });/*
20367  * Based on:
20368  * Ext JS Library 1.1.1
20369  * Copyright(c) 2006-2007, Ext JS, LLC.
20370  *
20371  * Originally Released Under LGPL - original licence link has changed is not relivant.
20372  *
20373  * Fork - LGPL
20374  * <script type="text/javascript">
20375  */
20376
20377 /**
20378  * @class Roo.data.ScriptTagProxy
20379  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
20380  * other than the originating domain of the running page.<br><br>
20381  * <p>
20382  * <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
20383  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
20384  * <p>
20385  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
20386  * source code that is used as the source inside a &lt;script> tag.<br><br>
20387  * <p>
20388  * In order for the browser to process the returned data, the server must wrap the data object
20389  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
20390  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
20391  * depending on whether the callback name was passed:
20392  * <p>
20393  * <pre><code>
20394 boolean scriptTag = false;
20395 String cb = request.getParameter("callback");
20396 if (cb != null) {
20397     scriptTag = true;
20398     response.setContentType("text/javascript");
20399 } else {
20400     response.setContentType("application/x-json");
20401 }
20402 Writer out = response.getWriter();
20403 if (scriptTag) {
20404     out.write(cb + "(");
20405 }
20406 out.print(dataBlock.toJsonString());
20407 if (scriptTag) {
20408     out.write(");");
20409 }
20410 </pre></code>
20411  *
20412  * @constructor
20413  * @param {Object} config A configuration object.
20414  */
20415 Roo.data.ScriptTagProxy = function(config){
20416     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
20417     Roo.apply(this, config);
20418     this.head = document.getElementsByTagName("head")[0];
20419 };
20420
20421 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
20422
20423 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
20424     /**
20425      * @cfg {String} url The URL from which to request the data object.
20426      */
20427     /**
20428      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
20429      */
20430     timeout : 30000,
20431     /**
20432      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
20433      * the server the name of the callback function set up by the load call to process the returned data object.
20434      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
20435      * javascript output which calls this named function passing the data object as its only parameter.
20436      */
20437     callbackParam : "callback",
20438     /**
20439      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
20440      * name to the request.
20441      */
20442     nocache : true,
20443
20444     /**
20445      * Load data from the configured URL, read the data object into
20446      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
20447      * process that block using the passed callback.
20448      * @param {Object} params An object containing properties which are to be used as HTTP parameters
20449      * for the request to the remote server.
20450      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20451      * object into a block of Roo.data.Records.
20452      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
20453      * The function must be passed <ul>
20454      * <li>The Record block object</li>
20455      * <li>The "arg" argument from the load function</li>
20456      * <li>A boolean success indicator</li>
20457      * </ul>
20458      * @param {Object} scope The scope in which to call the callback
20459      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20460      */
20461     load : function(params, reader, callback, scope, arg){
20462         if(this.fireEvent("beforeload", this, params) !== false){
20463
20464             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
20465
20466             var url = this.url;
20467             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
20468             if(this.nocache){
20469                 url += "&_dc=" + (new Date().getTime());
20470             }
20471             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
20472             var trans = {
20473                 id : transId,
20474                 cb : "stcCallback"+transId,
20475                 scriptId : "stcScript"+transId,
20476                 params : params,
20477                 arg : arg,
20478                 url : url,
20479                 callback : callback,
20480                 scope : scope,
20481                 reader : reader
20482             };
20483             var conn = this;
20484
20485             window[trans.cb] = function(o){
20486                 conn.handleResponse(o, trans);
20487             };
20488
20489             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
20490
20491             if(this.autoAbort !== false){
20492                 this.abort();
20493             }
20494
20495             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
20496
20497             var script = document.createElement("script");
20498             script.setAttribute("src", url);
20499             script.setAttribute("type", "text/javascript");
20500             script.setAttribute("id", trans.scriptId);
20501             this.head.appendChild(script);
20502
20503             this.trans = trans;
20504         }else{
20505             callback.call(scope||this, null, arg, false);
20506         }
20507     },
20508
20509     // private
20510     isLoading : function(){
20511         return this.trans ? true : false;
20512     },
20513
20514     /**
20515      * Abort the current server request.
20516      */
20517     abort : function(){
20518         if(this.isLoading()){
20519             this.destroyTrans(this.trans);
20520         }
20521     },
20522
20523     // private
20524     destroyTrans : function(trans, isLoaded){
20525         this.head.removeChild(document.getElementById(trans.scriptId));
20526         clearTimeout(trans.timeoutId);
20527         if(isLoaded){
20528             window[trans.cb] = undefined;
20529             try{
20530                 delete window[trans.cb];
20531             }catch(e){}
20532         }else{
20533             // if hasn't been loaded, wait for load to remove it to prevent script error
20534             window[trans.cb] = function(){
20535                 window[trans.cb] = undefined;
20536                 try{
20537                     delete window[trans.cb];
20538                 }catch(e){}
20539             };
20540         }
20541     },
20542
20543     // private
20544     handleResponse : function(o, trans){
20545         this.trans = false;
20546         this.destroyTrans(trans, true);
20547         var result;
20548         try {
20549             result = trans.reader.readRecords(o);
20550         }catch(e){
20551             this.fireEvent("loadexception", this, o, trans.arg, e);
20552             trans.callback.call(trans.scope||window, null, trans.arg, false);
20553             return;
20554         }
20555         this.fireEvent("load", this, o, trans.arg);
20556         trans.callback.call(trans.scope||window, result, trans.arg, true);
20557     },
20558
20559     // private
20560     handleFailure : function(trans){
20561         this.trans = false;
20562         this.destroyTrans(trans, false);
20563         this.fireEvent("loadexception", this, null, trans.arg);
20564         trans.callback.call(trans.scope||window, null, trans.arg, false);
20565     }
20566 });/*
20567  * Based on:
20568  * Ext JS Library 1.1.1
20569  * Copyright(c) 2006-2007, Ext JS, LLC.
20570  *
20571  * Originally Released Under LGPL - original licence link has changed is not relivant.
20572  *
20573  * Fork - LGPL
20574  * <script type="text/javascript">
20575  */
20576
20577 /**
20578  * @class Roo.data.JsonReader
20579  * @extends Roo.data.DataReader
20580  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
20581  * based on mappings in a provided Roo.data.Record constructor.
20582  * 
20583  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
20584  * in the reply previously. 
20585  * 
20586  * <p>
20587  * Example code:
20588  * <pre><code>
20589 var RecordDef = Roo.data.Record.create([
20590     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
20591     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
20592 ]);
20593 var myReader = new Roo.data.JsonReader({
20594     totalProperty: "results",    // The property which contains the total dataset size (optional)
20595     root: "rows",                // The property which contains an Array of row objects
20596     id: "id"                     // The property within each row object that provides an ID for the record (optional)
20597 }, RecordDef);
20598 </code></pre>
20599  * <p>
20600  * This would consume a JSON file like this:
20601  * <pre><code>
20602 { 'results': 2, 'rows': [
20603     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
20604     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
20605 }
20606 </code></pre>
20607  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
20608  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
20609  * paged from the remote server.
20610  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
20611  * @cfg {String} root name of the property which contains the Array of row objects.
20612  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
20613  * @constructor
20614  * Create a new JsonReader
20615  * @param {Object} meta Metadata configuration options
20616  * @param {Object} recordType Either an Array of field definition objects,
20617  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
20618  */
20619 Roo.data.JsonReader = function(meta, recordType){
20620     
20621     meta = meta || {};
20622     // set some defaults:
20623     Roo.applyIf(meta, {
20624         totalProperty: 'total',
20625         successProperty : 'success',
20626         root : 'data',
20627         id : 'id'
20628     });
20629     
20630     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
20631 };
20632 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
20633     
20634     /**
20635      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
20636      * Used by Store query builder to append _requestMeta to params.
20637      * 
20638      */
20639     metaFromRemote : false,
20640     /**
20641      * This method is only used by a DataProxy which has retrieved data from a remote server.
20642      * @param {Object} response The XHR object which contains the JSON data in its responseText.
20643      * @return {Object} data A data block which is used by an Roo.data.Store object as
20644      * a cache of Roo.data.Records.
20645      */
20646     read : function(response){
20647         var json = response.responseText;
20648        
20649         var o = /* eval:var:o */ eval("("+json+")");
20650         if(!o) {
20651             throw {message: "JsonReader.read: Json object not found"};
20652         }
20653         
20654         if(o.metaData){
20655             
20656             delete this.ef;
20657             this.metaFromRemote = true;
20658             this.meta = o.metaData;
20659             this.recordType = Roo.data.Record.create(o.metaData.fields);
20660             this.onMetaChange(this.meta, this.recordType, o);
20661         }
20662         return this.readRecords(o);
20663     },
20664
20665     // private function a store will implement
20666     onMetaChange : function(meta, recordType, o){
20667
20668     },
20669
20670     /**
20671          * @ignore
20672          */
20673     simpleAccess: function(obj, subsc) {
20674         return obj[subsc];
20675     },
20676
20677         /**
20678          * @ignore
20679          */
20680     getJsonAccessor: function(){
20681         var re = /[\[\.]/;
20682         return function(expr) {
20683             try {
20684                 return(re.test(expr))
20685                     ? new Function("obj", "return obj." + expr)
20686                     : function(obj){
20687                         return obj[expr];
20688                     };
20689             } catch(e){}
20690             return Roo.emptyFn;
20691         };
20692     }(),
20693
20694     /**
20695      * Create a data block containing Roo.data.Records from an XML document.
20696      * @param {Object} o An object which contains an Array of row objects in the property specified
20697      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
20698      * which contains the total size of the dataset.
20699      * @return {Object} data A data block which is used by an Roo.data.Store object as
20700      * a cache of Roo.data.Records.
20701      */
20702     readRecords : function(o){
20703         /**
20704          * After any data loads, the raw JSON data is available for further custom processing.
20705          * @type Object
20706          */
20707         this.jsonData = o;
20708         var s = this.meta, Record = this.recordType,
20709             f = Record.prototype.fields, fi = f.items, fl = f.length;
20710
20711 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
20712         if (!this.ef) {
20713             if(s.totalProperty) {
20714                     this.getTotal = this.getJsonAccessor(s.totalProperty);
20715                 }
20716                 if(s.successProperty) {
20717                     this.getSuccess = this.getJsonAccessor(s.successProperty);
20718                 }
20719                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
20720                 if (s.id) {
20721                         var g = this.getJsonAccessor(s.id);
20722                         this.getId = function(rec) {
20723                                 var r = g(rec);
20724                                 return (r === undefined || r === "") ? null : r;
20725                         };
20726                 } else {
20727                         this.getId = function(){return null;};
20728                 }
20729             this.ef = [];
20730             for(var jj = 0; jj < fl; jj++){
20731                 f = fi[jj];
20732                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
20733                 this.ef[jj] = this.getJsonAccessor(map);
20734             }
20735         }
20736
20737         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
20738         if(s.totalProperty){
20739             var vt = parseInt(this.getTotal(o), 10);
20740             if(!isNaN(vt)){
20741                 totalRecords = vt;
20742             }
20743         }
20744         if(s.successProperty){
20745             var vs = this.getSuccess(o);
20746             if(vs === false || vs === 'false'){
20747                 success = false;
20748             }
20749         }
20750         var records = [];
20751             for(var i = 0; i < c; i++){
20752                     var n = root[i];
20753                 var values = {};
20754                 var id = this.getId(n);
20755                 for(var j = 0; j < fl; j++){
20756                     f = fi[j];
20757                 var v = this.ef[j](n);
20758                 if (!f.convert) {
20759                     Roo.log('missing convert for ' + f.name);
20760                     Roo.log(f);
20761                     continue;
20762                 }
20763                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
20764                 }
20765                 var record = new Record(values, id);
20766                 record.json = n;
20767                 records[i] = record;
20768             }
20769             return {
20770                 success : success,
20771                 records : records,
20772                 totalRecords : totalRecords
20773             };
20774     }
20775 });/*
20776  * Based on:
20777  * Ext JS Library 1.1.1
20778  * Copyright(c) 2006-2007, Ext JS, LLC.
20779  *
20780  * Originally Released Under LGPL - original licence link has changed is not relivant.
20781  *
20782  * Fork - LGPL
20783  * <script type="text/javascript">
20784  */
20785
20786 /**
20787  * @class Roo.data.XmlReader
20788  * @extends Roo.data.DataReader
20789  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
20790  * based on mappings in a provided Roo.data.Record constructor.<br><br>
20791  * <p>
20792  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
20793  * header in the HTTP response must be set to "text/xml".</em>
20794  * <p>
20795  * Example code:
20796  * <pre><code>
20797 var RecordDef = Roo.data.Record.create([
20798    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
20799    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
20800 ]);
20801 var myReader = new Roo.data.XmlReader({
20802    totalRecords: "results", // The element which contains the total dataset size (optional)
20803    record: "row",           // The repeated element which contains row information
20804    id: "id"                 // The element within the row that provides an ID for the record (optional)
20805 }, RecordDef);
20806 </code></pre>
20807  * <p>
20808  * This would consume an XML file like this:
20809  * <pre><code>
20810 &lt;?xml?>
20811 &lt;dataset>
20812  &lt;results>2&lt;/results>
20813  &lt;row>
20814    &lt;id>1&lt;/id>
20815    &lt;name>Bill&lt;/name>
20816    &lt;occupation>Gardener&lt;/occupation>
20817  &lt;/row>
20818  &lt;row>
20819    &lt;id>2&lt;/id>
20820    &lt;name>Ben&lt;/name>
20821    &lt;occupation>Horticulturalist&lt;/occupation>
20822  &lt;/row>
20823 &lt;/dataset>
20824 </code></pre>
20825  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
20826  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
20827  * paged from the remote server.
20828  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
20829  * @cfg {String} success The DomQuery path to the success attribute used by forms.
20830  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
20831  * a record identifier value.
20832  * @constructor
20833  * Create a new XmlReader
20834  * @param {Object} meta Metadata configuration options
20835  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
20836  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
20837  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
20838  */
20839 Roo.data.XmlReader = function(meta, recordType){
20840     meta = meta || {};
20841     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
20842 };
20843 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
20844     /**
20845      * This method is only used by a DataProxy which has retrieved data from a remote server.
20846          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
20847          * to contain a method called 'responseXML' that returns an XML document object.
20848      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
20849      * a cache of Roo.data.Records.
20850      */
20851     read : function(response){
20852         var doc = response.responseXML;
20853         if(!doc) {
20854             throw {message: "XmlReader.read: XML Document not available"};
20855         }
20856         return this.readRecords(doc);
20857     },
20858
20859     /**
20860      * Create a data block containing Roo.data.Records from an XML document.
20861          * @param {Object} doc A parsed XML document.
20862      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
20863      * a cache of Roo.data.Records.
20864      */
20865     readRecords : function(doc){
20866         /**
20867          * After any data loads/reads, the raw XML Document is available for further custom processing.
20868          * @type XMLDocument
20869          */
20870         this.xmlData = doc;
20871         var root = doc.documentElement || doc;
20872         var q = Roo.DomQuery;
20873         var recordType = this.recordType, fields = recordType.prototype.fields;
20874         var sid = this.meta.id;
20875         var totalRecords = 0, success = true;
20876         if(this.meta.totalRecords){
20877             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
20878         }
20879         
20880         if(this.meta.success){
20881             var sv = q.selectValue(this.meta.success, root, true);
20882             success = sv !== false && sv !== 'false';
20883         }
20884         var records = [];
20885         var ns = q.select(this.meta.record, root);
20886         for(var i = 0, len = ns.length; i < len; i++) {
20887                 var n = ns[i];
20888                 var values = {};
20889                 var id = sid ? q.selectValue(sid, n) : undefined;
20890                 for(var j = 0, jlen = fields.length; j < jlen; j++){
20891                     var f = fields.items[j];
20892                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
20893                     v = f.convert(v);
20894                     values[f.name] = v;
20895                 }
20896                 var record = new recordType(values, id);
20897                 record.node = n;
20898                 records[records.length] = record;
20899             }
20900
20901             return {
20902                 success : success,
20903                 records : records,
20904                 totalRecords : totalRecords || records.length
20905             };
20906     }
20907 });/*
20908  * Based on:
20909  * Ext JS Library 1.1.1
20910  * Copyright(c) 2006-2007, Ext JS, LLC.
20911  *
20912  * Originally Released Under LGPL - original licence link has changed is not relivant.
20913  *
20914  * Fork - LGPL
20915  * <script type="text/javascript">
20916  */
20917
20918 /**
20919  * @class Roo.data.ArrayReader
20920  * @extends Roo.data.DataReader
20921  * Data reader class to create an Array of Roo.data.Record objects from an Array.
20922  * Each element of that Array represents a row of data fields. The
20923  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
20924  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
20925  * <p>
20926  * Example code:.
20927  * <pre><code>
20928 var RecordDef = Roo.data.Record.create([
20929     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
20930     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
20931 ]);
20932 var myReader = new Roo.data.ArrayReader({
20933     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
20934 }, RecordDef);
20935 </code></pre>
20936  * <p>
20937  * This would consume an Array like this:
20938  * <pre><code>
20939 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
20940   </code></pre>
20941  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
20942  * @constructor
20943  * Create a new JsonReader
20944  * @param {Object} meta Metadata configuration options.
20945  * @param {Object} recordType Either an Array of field definition objects
20946  * as specified to {@link Roo.data.Record#create},
20947  * or an {@link Roo.data.Record} object
20948  * created using {@link Roo.data.Record#create}.
20949  */
20950 Roo.data.ArrayReader = function(meta, recordType){
20951     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
20952 };
20953
20954 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
20955     /**
20956      * Create a data block containing Roo.data.Records from an XML document.
20957      * @param {Object} o An Array of row objects which represents the dataset.
20958      * @return {Object} data A data block which is used by an Roo.data.Store object as
20959      * a cache of Roo.data.Records.
20960      */
20961     readRecords : function(o){
20962         var sid = this.meta ? this.meta.id : null;
20963         var recordType = this.recordType, fields = recordType.prototype.fields;
20964         var records = [];
20965         var root = o;
20966             for(var i = 0; i < root.length; i++){
20967                     var n = root[i];
20968                 var values = {};
20969                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
20970                 for(var j = 0, jlen = fields.length; j < jlen; j++){
20971                 var f = fields.items[j];
20972                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
20973                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
20974                 v = f.convert(v);
20975                 values[f.name] = v;
20976             }
20977                 var record = new recordType(values, id);
20978                 record.json = n;
20979                 records[records.length] = record;
20980             }
20981             return {
20982                 records : records,
20983                 totalRecords : records.length
20984             };
20985     }
20986 });/*
20987  * Based on:
20988  * Ext JS Library 1.1.1
20989  * Copyright(c) 2006-2007, Ext JS, LLC.
20990  *
20991  * Originally Released Under LGPL - original licence link has changed is not relivant.
20992  *
20993  * Fork - LGPL
20994  * <script type="text/javascript">
20995  */
20996
20997
20998 /**
20999  * @class Roo.data.Tree
21000  * @extends Roo.util.Observable
21001  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
21002  * in the tree have most standard DOM functionality.
21003  * @constructor
21004  * @param {Node} root (optional) The root node
21005  */
21006 Roo.data.Tree = function(root){
21007    this.nodeHash = {};
21008    /**
21009     * The root node for this tree
21010     * @type Node
21011     */
21012    this.root = null;
21013    if(root){
21014        this.setRootNode(root);
21015    }
21016    this.addEvents({
21017        /**
21018         * @event append
21019         * Fires when a new child node is appended to a node in this tree.
21020         * @param {Tree} tree The owner tree
21021         * @param {Node} parent The parent node
21022         * @param {Node} node The newly appended node
21023         * @param {Number} index The index of the newly appended node
21024         */
21025        "append" : true,
21026        /**
21027         * @event remove
21028         * Fires when a child node is removed from a node in this tree.
21029         * @param {Tree} tree The owner tree
21030         * @param {Node} parent The parent node
21031         * @param {Node} node The child node removed
21032         */
21033        "remove" : true,
21034        /**
21035         * @event move
21036         * Fires when a node is moved to a new location in the tree
21037         * @param {Tree} tree The owner tree
21038         * @param {Node} node The node moved
21039         * @param {Node} oldParent The old parent of this node
21040         * @param {Node} newParent The new parent of this node
21041         * @param {Number} index The index it was moved to
21042         */
21043        "move" : true,
21044        /**
21045         * @event insert
21046         * Fires when a new child node is inserted in a node in this tree.
21047         * @param {Tree} tree The owner tree
21048         * @param {Node} parent The parent node
21049         * @param {Node} node The child node inserted
21050         * @param {Node} refNode The child node the node was inserted before
21051         */
21052        "insert" : true,
21053        /**
21054         * @event beforeappend
21055         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
21056         * @param {Tree} tree The owner tree
21057         * @param {Node} parent The parent node
21058         * @param {Node} node The child node to be appended
21059         */
21060        "beforeappend" : true,
21061        /**
21062         * @event beforeremove
21063         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
21064         * @param {Tree} tree The owner tree
21065         * @param {Node} parent The parent node
21066         * @param {Node} node The child node to be removed
21067         */
21068        "beforeremove" : true,
21069        /**
21070         * @event beforemove
21071         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
21072         * @param {Tree} tree The owner tree
21073         * @param {Node} node The node being moved
21074         * @param {Node} oldParent The parent of the node
21075         * @param {Node} newParent The new parent the node is moving to
21076         * @param {Number} index The index it is being moved to
21077         */
21078        "beforemove" : true,
21079        /**
21080         * @event beforeinsert
21081         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
21082         * @param {Tree} tree The owner tree
21083         * @param {Node} parent The parent node
21084         * @param {Node} node The child node to be inserted
21085         * @param {Node} refNode The child node the node is being inserted before
21086         */
21087        "beforeinsert" : true
21088    });
21089
21090     Roo.data.Tree.superclass.constructor.call(this);
21091 };
21092
21093 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
21094     pathSeparator: "/",
21095
21096     proxyNodeEvent : function(){
21097         return this.fireEvent.apply(this, arguments);
21098     },
21099
21100     /**
21101      * Returns the root node for this tree.
21102      * @return {Node}
21103      */
21104     getRootNode : function(){
21105         return this.root;
21106     },
21107
21108     /**
21109      * Sets the root node for this tree.
21110      * @param {Node} node
21111      * @return {Node}
21112      */
21113     setRootNode : function(node){
21114         this.root = node;
21115         node.ownerTree = this;
21116         node.isRoot = true;
21117         this.registerNode(node);
21118         return node;
21119     },
21120
21121     /**
21122      * Gets a node in this tree by its id.
21123      * @param {String} id
21124      * @return {Node}
21125      */
21126     getNodeById : function(id){
21127         return this.nodeHash[id];
21128     },
21129
21130     registerNode : function(node){
21131         this.nodeHash[node.id] = node;
21132     },
21133
21134     unregisterNode : function(node){
21135         delete this.nodeHash[node.id];
21136     },
21137
21138     toString : function(){
21139         return "[Tree"+(this.id?" "+this.id:"")+"]";
21140     }
21141 });
21142
21143 /**
21144  * @class Roo.data.Node
21145  * @extends Roo.util.Observable
21146  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
21147  * @cfg {String} id The id for this node. If one is not specified, one is generated.
21148  * @constructor
21149  * @param {Object} attributes The attributes/config for the node
21150  */
21151 Roo.data.Node = function(attributes){
21152     /**
21153      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
21154      * @type {Object}
21155      */
21156     this.attributes = attributes || {};
21157     this.leaf = this.attributes.leaf;
21158     /**
21159      * The node id. @type String
21160      */
21161     this.id = this.attributes.id;
21162     if(!this.id){
21163         this.id = Roo.id(null, "ynode-");
21164         this.attributes.id = this.id;
21165     }
21166     /**
21167      * All child nodes of this node. @type Array
21168      */
21169     this.childNodes = [];
21170     if(!this.childNodes.indexOf){ // indexOf is a must
21171         this.childNodes.indexOf = function(o){
21172             for(var i = 0, len = this.length; i < len; i++){
21173                 if(this[i] == o) {
21174                     return i;
21175                 }
21176             }
21177             return -1;
21178         };
21179     }
21180     /**
21181      * The parent node for this node. @type Node
21182      */
21183     this.parentNode = null;
21184     /**
21185      * The first direct child node of this node, or null if this node has no child nodes. @type Node
21186      */
21187     this.firstChild = null;
21188     /**
21189      * The last direct child node of this node, or null if this node has no child nodes. @type Node
21190      */
21191     this.lastChild = null;
21192     /**
21193      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
21194      */
21195     this.previousSibling = null;
21196     /**
21197      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
21198      */
21199     this.nextSibling = null;
21200
21201     this.addEvents({
21202        /**
21203         * @event append
21204         * Fires when a new child node is appended
21205         * @param {Tree} tree The owner tree
21206         * @param {Node} this This node
21207         * @param {Node} node The newly appended node
21208         * @param {Number} index The index of the newly appended node
21209         */
21210        "append" : true,
21211        /**
21212         * @event remove
21213         * Fires when a child node is removed
21214         * @param {Tree} tree The owner tree
21215         * @param {Node} this This node
21216         * @param {Node} node The removed node
21217         */
21218        "remove" : true,
21219        /**
21220         * @event move
21221         * Fires when this node is moved to a new location in the tree
21222         * @param {Tree} tree The owner tree
21223         * @param {Node} this This node
21224         * @param {Node} oldParent The old parent of this node
21225         * @param {Node} newParent The new parent of this node
21226         * @param {Number} index The index it was moved to
21227         */
21228        "move" : true,
21229        /**
21230         * @event insert
21231         * Fires when a new child node is inserted.
21232         * @param {Tree} tree The owner tree
21233         * @param {Node} this This node
21234         * @param {Node} node The child node inserted
21235         * @param {Node} refNode The child node the node was inserted before
21236         */
21237        "insert" : true,
21238        /**
21239         * @event beforeappend
21240         * Fires before a new child is appended, return false to cancel the append.
21241         * @param {Tree} tree The owner tree
21242         * @param {Node} this This node
21243         * @param {Node} node The child node to be appended
21244         */
21245        "beforeappend" : true,
21246        /**
21247         * @event beforeremove
21248         * Fires before a child is removed, return false to cancel the remove.
21249         * @param {Tree} tree The owner tree
21250         * @param {Node} this This node
21251         * @param {Node} node The child node to be removed
21252         */
21253        "beforeremove" : true,
21254        /**
21255         * @event beforemove
21256         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
21257         * @param {Tree} tree The owner tree
21258         * @param {Node} this This node
21259         * @param {Node} oldParent The parent of this node
21260         * @param {Node} newParent The new parent this node is moving to
21261         * @param {Number} index The index it is being moved to
21262         */
21263        "beforemove" : true,
21264        /**
21265         * @event beforeinsert
21266         * Fires before a new child is inserted, return false to cancel the insert.
21267         * @param {Tree} tree The owner tree
21268         * @param {Node} this This node
21269         * @param {Node} node The child node to be inserted
21270         * @param {Node} refNode The child node the node is being inserted before
21271         */
21272        "beforeinsert" : true
21273    });
21274     this.listeners = this.attributes.listeners;
21275     Roo.data.Node.superclass.constructor.call(this);
21276 };
21277
21278 Roo.extend(Roo.data.Node, Roo.util.Observable, {
21279     fireEvent : function(evtName){
21280         // first do standard event for this node
21281         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
21282             return false;
21283         }
21284         // then bubble it up to the tree if the event wasn't cancelled
21285         var ot = this.getOwnerTree();
21286         if(ot){
21287             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
21288                 return false;
21289             }
21290         }
21291         return true;
21292     },
21293
21294     /**
21295      * Returns true if this node is a leaf
21296      * @return {Boolean}
21297      */
21298     isLeaf : function(){
21299         return this.leaf === true;
21300     },
21301
21302     // private
21303     setFirstChild : function(node){
21304         this.firstChild = node;
21305     },
21306
21307     //private
21308     setLastChild : function(node){
21309         this.lastChild = node;
21310     },
21311
21312
21313     /**
21314      * Returns true if this node is the last child of its parent
21315      * @return {Boolean}
21316      */
21317     isLast : function(){
21318        return (!this.parentNode ? true : this.parentNode.lastChild == this);
21319     },
21320
21321     /**
21322      * Returns true if this node is the first child of its parent
21323      * @return {Boolean}
21324      */
21325     isFirst : function(){
21326        return (!this.parentNode ? true : this.parentNode.firstChild == this);
21327     },
21328
21329     hasChildNodes : function(){
21330         return !this.isLeaf() && this.childNodes.length > 0;
21331     },
21332
21333     /**
21334      * Insert node(s) as the last child node of this node.
21335      * @param {Node/Array} node The node or Array of nodes to append
21336      * @return {Node} The appended node if single append, or null if an array was passed
21337      */
21338     appendChild : function(node){
21339         var multi = false;
21340         if(node instanceof Array){
21341             multi = node;
21342         }else if(arguments.length > 1){
21343             multi = arguments;
21344         }
21345         // if passed an array or multiple args do them one by one
21346         if(multi){
21347             for(var i = 0, len = multi.length; i < len; i++) {
21348                 this.appendChild(multi[i]);
21349             }
21350         }else{
21351             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
21352                 return false;
21353             }
21354             var index = this.childNodes.length;
21355             var oldParent = node.parentNode;
21356             // it's a move, make sure we move it cleanly
21357             if(oldParent){
21358                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
21359                     return false;
21360                 }
21361                 oldParent.removeChild(node);
21362             }
21363             index = this.childNodes.length;
21364             if(index == 0){
21365                 this.setFirstChild(node);
21366             }
21367             this.childNodes.push(node);
21368             node.parentNode = this;
21369             var ps = this.childNodes[index-1];
21370             if(ps){
21371                 node.previousSibling = ps;
21372                 ps.nextSibling = node;
21373             }else{
21374                 node.previousSibling = null;
21375             }
21376             node.nextSibling = null;
21377             this.setLastChild(node);
21378             node.setOwnerTree(this.getOwnerTree());
21379             this.fireEvent("append", this.ownerTree, this, node, index);
21380             if(oldParent){
21381                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
21382             }
21383             return node;
21384         }
21385     },
21386
21387     /**
21388      * Removes a child node from this node.
21389      * @param {Node} node The node to remove
21390      * @return {Node} The removed node
21391      */
21392     removeChild : function(node){
21393         var index = this.childNodes.indexOf(node);
21394         if(index == -1){
21395             return false;
21396         }
21397         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
21398             return false;
21399         }
21400
21401         // remove it from childNodes collection
21402         this.childNodes.splice(index, 1);
21403
21404         // update siblings
21405         if(node.previousSibling){
21406             node.previousSibling.nextSibling = node.nextSibling;
21407         }
21408         if(node.nextSibling){
21409             node.nextSibling.previousSibling = node.previousSibling;
21410         }
21411
21412         // update child refs
21413         if(this.firstChild == node){
21414             this.setFirstChild(node.nextSibling);
21415         }
21416         if(this.lastChild == node){
21417             this.setLastChild(node.previousSibling);
21418         }
21419
21420         node.setOwnerTree(null);
21421         // clear any references from the node
21422         node.parentNode = null;
21423         node.previousSibling = null;
21424         node.nextSibling = null;
21425         this.fireEvent("remove", this.ownerTree, this, node);
21426         return node;
21427     },
21428
21429     /**
21430      * Inserts the first node before the second node in this nodes childNodes collection.
21431      * @param {Node} node The node to insert
21432      * @param {Node} refNode The node to insert before (if null the node is appended)
21433      * @return {Node} The inserted node
21434      */
21435     insertBefore : function(node, refNode){
21436         if(!refNode){ // like standard Dom, refNode can be null for append
21437             return this.appendChild(node);
21438         }
21439         // nothing to do
21440         if(node == refNode){
21441             return false;
21442         }
21443
21444         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
21445             return false;
21446         }
21447         var index = this.childNodes.indexOf(refNode);
21448         var oldParent = node.parentNode;
21449         var refIndex = index;
21450
21451         // when moving internally, indexes will change after remove
21452         if(oldParent == this && this.childNodes.indexOf(node) < index){
21453             refIndex--;
21454         }
21455
21456         // it's a move, make sure we move it cleanly
21457         if(oldParent){
21458             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
21459                 return false;
21460             }
21461             oldParent.removeChild(node);
21462         }
21463         if(refIndex == 0){
21464             this.setFirstChild(node);
21465         }
21466         this.childNodes.splice(refIndex, 0, node);
21467         node.parentNode = this;
21468         var ps = this.childNodes[refIndex-1];
21469         if(ps){
21470             node.previousSibling = ps;
21471             ps.nextSibling = node;
21472         }else{
21473             node.previousSibling = null;
21474         }
21475         node.nextSibling = refNode;
21476         refNode.previousSibling = node;
21477         node.setOwnerTree(this.getOwnerTree());
21478         this.fireEvent("insert", this.ownerTree, this, node, refNode);
21479         if(oldParent){
21480             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
21481         }
21482         return node;
21483     },
21484
21485     /**
21486      * Returns the child node at the specified index.
21487      * @param {Number} index
21488      * @return {Node}
21489      */
21490     item : function(index){
21491         return this.childNodes[index];
21492     },
21493
21494     /**
21495      * Replaces one child node in this node with another.
21496      * @param {Node} newChild The replacement node
21497      * @param {Node} oldChild The node to replace
21498      * @return {Node} The replaced node
21499      */
21500     replaceChild : function(newChild, oldChild){
21501         this.insertBefore(newChild, oldChild);
21502         this.removeChild(oldChild);
21503         return oldChild;
21504     },
21505
21506     /**
21507      * Returns the index of a child node
21508      * @param {Node} node
21509      * @return {Number} The index of the node or -1 if it was not found
21510      */
21511     indexOf : function(child){
21512         return this.childNodes.indexOf(child);
21513     },
21514
21515     /**
21516      * Returns the tree this node is in.
21517      * @return {Tree}
21518      */
21519     getOwnerTree : function(){
21520         // if it doesn't have one, look for one
21521         if(!this.ownerTree){
21522             var p = this;
21523             while(p){
21524                 if(p.ownerTree){
21525                     this.ownerTree = p.ownerTree;
21526                     break;
21527                 }
21528                 p = p.parentNode;
21529             }
21530         }
21531         return this.ownerTree;
21532     },
21533
21534     /**
21535      * Returns depth of this node (the root node has a depth of 0)
21536      * @return {Number}
21537      */
21538     getDepth : function(){
21539         var depth = 0;
21540         var p = this;
21541         while(p.parentNode){
21542             ++depth;
21543             p = p.parentNode;
21544         }
21545         return depth;
21546     },
21547
21548     // private
21549     setOwnerTree : function(tree){
21550         // if it's move, we need to update everyone
21551         if(tree != this.ownerTree){
21552             if(this.ownerTree){
21553                 this.ownerTree.unregisterNode(this);
21554             }
21555             this.ownerTree = tree;
21556             var cs = this.childNodes;
21557             for(var i = 0, len = cs.length; i < len; i++) {
21558                 cs[i].setOwnerTree(tree);
21559             }
21560             if(tree){
21561                 tree.registerNode(this);
21562             }
21563         }
21564     },
21565
21566     /**
21567      * Returns the path for this node. The path can be used to expand or select this node programmatically.
21568      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
21569      * @return {String} The path
21570      */
21571     getPath : function(attr){
21572         attr = attr || "id";
21573         var p = this.parentNode;
21574         var b = [this.attributes[attr]];
21575         while(p){
21576             b.unshift(p.attributes[attr]);
21577             p = p.parentNode;
21578         }
21579         var sep = this.getOwnerTree().pathSeparator;
21580         return sep + b.join(sep);
21581     },
21582
21583     /**
21584      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
21585      * function call will be the scope provided or the current node. The arguments to the function
21586      * will be the args provided or the current node. If the function returns false at any point,
21587      * the bubble is stopped.
21588      * @param {Function} fn The function to call
21589      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21590      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21591      */
21592     bubble : function(fn, scope, args){
21593         var p = this;
21594         while(p){
21595             if(fn.call(scope || p, args || p) === false){
21596                 break;
21597             }
21598             p = p.parentNode;
21599         }
21600     },
21601
21602     /**
21603      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
21604      * function call will be the scope provided or the current node. The arguments to the function
21605      * will be the args provided or the current node. If the function returns false at any point,
21606      * the cascade is stopped on that branch.
21607      * @param {Function} fn The function to call
21608      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21609      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21610      */
21611     cascade : function(fn, scope, args){
21612         if(fn.call(scope || this, args || this) !== false){
21613             var cs = this.childNodes;
21614             for(var i = 0, len = cs.length; i < len; i++) {
21615                 cs[i].cascade(fn, scope, args);
21616             }
21617         }
21618     },
21619
21620     /**
21621      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
21622      * function call will be the scope provided or the current node. The arguments to the function
21623      * will be the args provided or the current node. If the function returns false at any point,
21624      * the iteration stops.
21625      * @param {Function} fn The function to call
21626      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21627      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21628      */
21629     eachChild : function(fn, scope, args){
21630         var cs = this.childNodes;
21631         for(var i = 0, len = cs.length; i < len; i++) {
21632                 if(fn.call(scope || this, args || cs[i]) === false){
21633                     break;
21634                 }
21635         }
21636     },
21637
21638     /**
21639      * Finds the first child that has the attribute with the specified value.
21640      * @param {String} attribute The attribute name
21641      * @param {Mixed} value The value to search for
21642      * @return {Node} The found child or null if none was found
21643      */
21644     findChild : function(attribute, value){
21645         var cs = this.childNodes;
21646         for(var i = 0, len = cs.length; i < len; i++) {
21647                 if(cs[i].attributes[attribute] == value){
21648                     return cs[i];
21649                 }
21650         }
21651         return null;
21652     },
21653
21654     /**
21655      * Finds the first child by a custom function. The child matches if the function passed
21656      * returns true.
21657      * @param {Function} fn
21658      * @param {Object} scope (optional)
21659      * @return {Node} The found child or null if none was found
21660      */
21661     findChildBy : function(fn, scope){
21662         var cs = this.childNodes;
21663         for(var i = 0, len = cs.length; i < len; i++) {
21664                 if(fn.call(scope||cs[i], cs[i]) === true){
21665                     return cs[i];
21666                 }
21667         }
21668         return null;
21669     },
21670
21671     /**
21672      * Sorts this nodes children using the supplied sort function
21673      * @param {Function} fn
21674      * @param {Object} scope (optional)
21675      */
21676     sort : function(fn, scope){
21677         var cs = this.childNodes;
21678         var len = cs.length;
21679         if(len > 0){
21680             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
21681             cs.sort(sortFn);
21682             for(var i = 0; i < len; i++){
21683                 var n = cs[i];
21684                 n.previousSibling = cs[i-1];
21685                 n.nextSibling = cs[i+1];
21686                 if(i == 0){
21687                     this.setFirstChild(n);
21688                 }
21689                 if(i == len-1){
21690                     this.setLastChild(n);
21691                 }
21692             }
21693         }
21694     },
21695
21696     /**
21697      * Returns true if this node is an ancestor (at any point) of the passed node.
21698      * @param {Node} node
21699      * @return {Boolean}
21700      */
21701     contains : function(node){
21702         return node.isAncestor(this);
21703     },
21704
21705     /**
21706      * Returns true if the passed node is an ancestor (at any point) of this node.
21707      * @param {Node} node
21708      * @return {Boolean}
21709      */
21710     isAncestor : function(node){
21711         var p = this.parentNode;
21712         while(p){
21713             if(p == node){
21714                 return true;
21715             }
21716             p = p.parentNode;
21717         }
21718         return false;
21719     },
21720
21721     toString : function(){
21722         return "[Node"+(this.id?" "+this.id:"")+"]";
21723     }
21724 });/*
21725  * Based on:
21726  * Ext JS Library 1.1.1
21727  * Copyright(c) 2006-2007, Ext JS, LLC.
21728  *
21729  * Originally Released Under LGPL - original licence link has changed is not relivant.
21730  *
21731  * Fork - LGPL
21732  * <script type="text/javascript">
21733  */
21734  
21735
21736 /**
21737  * @class Roo.ComponentMgr
21738  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
21739  * @singleton
21740  */
21741 Roo.ComponentMgr = function(){
21742     var all = new Roo.util.MixedCollection();
21743
21744     return {
21745         /**
21746          * Registers a component.
21747          * @param {Roo.Component} c The component
21748          */
21749         register : function(c){
21750             all.add(c);
21751         },
21752
21753         /**
21754          * Unregisters a component.
21755          * @param {Roo.Component} c The component
21756          */
21757         unregister : function(c){
21758             all.remove(c);
21759         },
21760
21761         /**
21762          * Returns a component by id
21763          * @param {String} id The component id
21764          */
21765         get : function(id){
21766             return all.get(id);
21767         },
21768
21769         /**
21770          * Registers a function that will be called when a specified component is added to ComponentMgr
21771          * @param {String} id The component id
21772          * @param {Funtction} fn The callback function
21773          * @param {Object} scope The scope of the callback
21774          */
21775         onAvailable : function(id, fn, scope){
21776             all.on("add", function(index, o){
21777                 if(o.id == id){
21778                     fn.call(scope || o, o);
21779                     all.un("add", fn, scope);
21780                 }
21781             });
21782         }
21783     };
21784 }();/*
21785  * Based on:
21786  * Ext JS Library 1.1.1
21787  * Copyright(c) 2006-2007, Ext JS, LLC.
21788  *
21789  * Originally Released Under LGPL - original licence link has changed is not relivant.
21790  *
21791  * Fork - LGPL
21792  * <script type="text/javascript">
21793  */
21794  
21795 /**
21796  * @class Roo.Component
21797  * @extends Roo.util.Observable
21798  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
21799  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
21800  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
21801  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
21802  * All visual components (widgets) that require rendering into a layout should subclass Component.
21803  * @constructor
21804  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
21805  * 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
21806  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
21807  */
21808 Roo.Component = function(config){
21809     config = config || {};
21810     if(config.tagName || config.dom || typeof config == "string"){ // element object
21811         config = {el: config, id: config.id || config};
21812     }
21813     this.initialConfig = config;
21814
21815     Roo.apply(this, config);
21816     this.addEvents({
21817         /**
21818          * @event disable
21819          * Fires after the component is disabled.
21820              * @param {Roo.Component} this
21821              */
21822         disable : true,
21823         /**
21824          * @event enable
21825          * Fires after the component is enabled.
21826              * @param {Roo.Component} this
21827              */
21828         enable : true,
21829         /**
21830          * @event beforeshow
21831          * Fires before the component is shown.  Return false to stop the show.
21832              * @param {Roo.Component} this
21833              */
21834         beforeshow : true,
21835         /**
21836          * @event show
21837          * Fires after the component is shown.
21838              * @param {Roo.Component} this
21839              */
21840         show : true,
21841         /**
21842          * @event beforehide
21843          * Fires before the component is hidden. Return false to stop the hide.
21844              * @param {Roo.Component} this
21845              */
21846         beforehide : true,
21847         /**
21848          * @event hide
21849          * Fires after the component is hidden.
21850              * @param {Roo.Component} this
21851              */
21852         hide : true,
21853         /**
21854          * @event beforerender
21855          * Fires before the component is rendered. Return false to stop the render.
21856              * @param {Roo.Component} this
21857              */
21858         beforerender : true,
21859         /**
21860          * @event render
21861          * Fires after the component is rendered.
21862              * @param {Roo.Component} this
21863              */
21864         render : true,
21865         /**
21866          * @event beforedestroy
21867          * Fires before the component is destroyed. Return false to stop the destroy.
21868              * @param {Roo.Component} this
21869              */
21870         beforedestroy : true,
21871         /**
21872          * @event destroy
21873          * Fires after the component is destroyed.
21874              * @param {Roo.Component} this
21875              */
21876         destroy : true
21877     });
21878     if(!this.id){
21879         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
21880     }
21881     Roo.ComponentMgr.register(this);
21882     Roo.Component.superclass.constructor.call(this);
21883     this.initComponent();
21884     if(this.renderTo){ // not supported by all components yet. use at your own risk!
21885         this.render(this.renderTo);
21886         delete this.renderTo;
21887     }
21888 };
21889
21890 /** @private */
21891 Roo.Component.AUTO_ID = 1000;
21892
21893 Roo.extend(Roo.Component, Roo.util.Observable, {
21894     /**
21895      * @scope Roo.Component.prototype
21896      * @type {Boolean}
21897      * true if this component is hidden. Read-only.
21898      */
21899     hidden : false,
21900     /**
21901      * @type {Boolean}
21902      * true if this component is disabled. Read-only.
21903      */
21904     disabled : false,
21905     /**
21906      * @type {Boolean}
21907      * true if this component has been rendered. Read-only.
21908      */
21909     rendered : false,
21910     
21911     /** @cfg {String} disableClass
21912      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
21913      */
21914     disabledClass : "x-item-disabled",
21915         /** @cfg {Boolean} allowDomMove
21916          * Whether the component can move the Dom node when rendering (defaults to true).
21917          */
21918     allowDomMove : true,
21919     /** @cfg {String} hideMode
21920      * How this component should hidden. Supported values are
21921      * "visibility" (css visibility), "offsets" (negative offset position) and
21922      * "display" (css display) - defaults to "display".
21923      */
21924     hideMode: 'display',
21925
21926     /** @private */
21927     ctype : "Roo.Component",
21928
21929     /**
21930      * @cfg {String} actionMode 
21931      * which property holds the element that used for  hide() / show() / disable() / enable()
21932      * default is 'el' 
21933      */
21934     actionMode : "el",
21935
21936     /** @private */
21937     getActionEl : function(){
21938         return this[this.actionMode];
21939     },
21940
21941     initComponent : Roo.emptyFn,
21942     /**
21943      * If this is a lazy rendering component, render it to its container element.
21944      * @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.
21945      */
21946     render : function(container, position){
21947         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
21948             if(!container && this.el){
21949                 this.el = Roo.get(this.el);
21950                 container = this.el.dom.parentNode;
21951                 this.allowDomMove = false;
21952             }
21953             this.container = Roo.get(container);
21954             this.rendered = true;
21955             if(position !== undefined){
21956                 if(typeof position == 'number'){
21957                     position = this.container.dom.childNodes[position];
21958                 }else{
21959                     position = Roo.getDom(position);
21960                 }
21961             }
21962             this.onRender(this.container, position || null);
21963             if(this.cls){
21964                 this.el.addClass(this.cls);
21965                 delete this.cls;
21966             }
21967             if(this.style){
21968                 this.el.applyStyles(this.style);
21969                 delete this.style;
21970             }
21971             this.fireEvent("render", this);
21972             this.afterRender(this.container);
21973             if(this.hidden){
21974                 this.hide();
21975             }
21976             if(this.disabled){
21977                 this.disable();
21978             }
21979         }
21980         return this;
21981     },
21982
21983     /** @private */
21984     // default function is not really useful
21985     onRender : function(ct, position){
21986         if(this.el){
21987             this.el = Roo.get(this.el);
21988             if(this.allowDomMove !== false){
21989                 ct.dom.insertBefore(this.el.dom, position);
21990             }
21991         }
21992     },
21993
21994     /** @private */
21995     getAutoCreate : function(){
21996         var cfg = typeof this.autoCreate == "object" ?
21997                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
21998         if(this.id && !cfg.id){
21999             cfg.id = this.id;
22000         }
22001         return cfg;
22002     },
22003
22004     /** @private */
22005     afterRender : Roo.emptyFn,
22006
22007     /**
22008      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
22009      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
22010      */
22011     destroy : function(){
22012         if(this.fireEvent("beforedestroy", this) !== false){
22013             this.purgeListeners();
22014             this.beforeDestroy();
22015             if(this.rendered){
22016                 this.el.removeAllListeners();
22017                 this.el.remove();
22018                 if(this.actionMode == "container"){
22019                     this.container.remove();
22020                 }
22021             }
22022             this.onDestroy();
22023             Roo.ComponentMgr.unregister(this);
22024             this.fireEvent("destroy", this);
22025         }
22026     },
22027
22028         /** @private */
22029     beforeDestroy : function(){
22030
22031     },
22032
22033         /** @private */
22034         onDestroy : function(){
22035
22036     },
22037
22038     /**
22039      * Returns the underlying {@link Roo.Element}.
22040      * @return {Roo.Element} The element
22041      */
22042     getEl : function(){
22043         return this.el;
22044     },
22045
22046     /**
22047      * Returns the id of this component.
22048      * @return {String}
22049      */
22050     getId : function(){
22051         return this.id;
22052     },
22053
22054     /**
22055      * Try to focus this component.
22056      * @param {Boolean} selectText True to also select the text in this component (if applicable)
22057      * @return {Roo.Component} this
22058      */
22059     focus : function(selectText){
22060         if(this.rendered){
22061             this.el.focus();
22062             if(selectText === true){
22063                 this.el.dom.select();
22064             }
22065         }
22066         return this;
22067     },
22068
22069     /** @private */
22070     blur : function(){
22071         if(this.rendered){
22072             this.el.blur();
22073         }
22074         return this;
22075     },
22076
22077     /**
22078      * Disable this component.
22079      * @return {Roo.Component} this
22080      */
22081     disable : function(){
22082         if(this.rendered){
22083             this.onDisable();
22084         }
22085         this.disabled = true;
22086         this.fireEvent("disable", this);
22087         return this;
22088     },
22089
22090         // private
22091     onDisable : function(){
22092         this.getActionEl().addClass(this.disabledClass);
22093         this.el.dom.disabled = true;
22094     },
22095
22096     /**
22097      * Enable this component.
22098      * @return {Roo.Component} this
22099      */
22100     enable : function(){
22101         if(this.rendered){
22102             this.onEnable();
22103         }
22104         this.disabled = false;
22105         this.fireEvent("enable", this);
22106         return this;
22107     },
22108
22109         // private
22110     onEnable : function(){
22111         this.getActionEl().removeClass(this.disabledClass);
22112         this.el.dom.disabled = false;
22113     },
22114
22115     /**
22116      * Convenience function for setting disabled/enabled by boolean.
22117      * @param {Boolean} disabled
22118      */
22119     setDisabled : function(disabled){
22120         this[disabled ? "disable" : "enable"]();
22121     },
22122
22123     /**
22124      * Show this component.
22125      * @return {Roo.Component} this
22126      */
22127     show: function(){
22128         if(this.fireEvent("beforeshow", this) !== false){
22129             this.hidden = false;
22130             if(this.rendered){
22131                 this.onShow();
22132             }
22133             this.fireEvent("show", this);
22134         }
22135         return this;
22136     },
22137
22138     // private
22139     onShow : function(){
22140         var ae = this.getActionEl();
22141         if(this.hideMode == 'visibility'){
22142             ae.dom.style.visibility = "visible";
22143         }else if(this.hideMode == 'offsets'){
22144             ae.removeClass('x-hidden');
22145         }else{
22146             ae.dom.style.display = "";
22147         }
22148     },
22149
22150     /**
22151      * Hide this component.
22152      * @return {Roo.Component} this
22153      */
22154     hide: function(){
22155         if(this.fireEvent("beforehide", this) !== false){
22156             this.hidden = true;
22157             if(this.rendered){
22158                 this.onHide();
22159             }
22160             this.fireEvent("hide", this);
22161         }
22162         return this;
22163     },
22164
22165     // private
22166     onHide : function(){
22167         var ae = this.getActionEl();
22168         if(this.hideMode == 'visibility'){
22169             ae.dom.style.visibility = "hidden";
22170         }else if(this.hideMode == 'offsets'){
22171             ae.addClass('x-hidden');
22172         }else{
22173             ae.dom.style.display = "none";
22174         }
22175     },
22176
22177     /**
22178      * Convenience function to hide or show this component by boolean.
22179      * @param {Boolean} visible True to show, false to hide
22180      * @return {Roo.Component} this
22181      */
22182     setVisible: function(visible){
22183         if(visible) {
22184             this.show();
22185         }else{
22186             this.hide();
22187         }
22188         return this;
22189     },
22190
22191     /**
22192      * Returns true if this component is visible.
22193      */
22194     isVisible : function(){
22195         return this.getActionEl().isVisible();
22196     },
22197
22198     cloneConfig : function(overrides){
22199         overrides = overrides || {};
22200         var id = overrides.id || Roo.id();
22201         var cfg = Roo.applyIf(overrides, this.initialConfig);
22202         cfg.id = id; // prevent dup id
22203         return new this.constructor(cfg);
22204     }
22205 });/*
22206  * Based on:
22207  * Ext JS Library 1.1.1
22208  * Copyright(c) 2006-2007, Ext JS, LLC.
22209  *
22210  * Originally Released Under LGPL - original licence link has changed is not relivant.
22211  *
22212  * Fork - LGPL
22213  * <script type="text/javascript">
22214  */
22215  (function(){ 
22216 /**
22217  * @class Roo.Layer
22218  * @extends Roo.Element
22219  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
22220  * automatic maintaining of shadow/shim positions.
22221  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
22222  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
22223  * you can pass a string with a CSS class name. False turns off the shadow.
22224  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
22225  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
22226  * @cfg {String} cls CSS class to add to the element
22227  * @cfg {Number} zindex Starting z-index (defaults to 11000)
22228  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
22229  * @constructor
22230  * @param {Object} config An object with config options.
22231  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
22232  */
22233
22234 Roo.Layer = function(config, existingEl){
22235     config = config || {};
22236     var dh = Roo.DomHelper;
22237     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
22238     if(existingEl){
22239         this.dom = Roo.getDom(existingEl);
22240     }
22241     if(!this.dom){
22242         var o = config.dh || {tag: "div", cls: "x-layer"};
22243         this.dom = dh.append(pel, o);
22244     }
22245     if(config.cls){
22246         this.addClass(config.cls);
22247     }
22248     this.constrain = config.constrain !== false;
22249     this.visibilityMode = Roo.Element.VISIBILITY;
22250     if(config.id){
22251         this.id = this.dom.id = config.id;
22252     }else{
22253         this.id = Roo.id(this.dom);
22254     }
22255     this.zindex = config.zindex || this.getZIndex();
22256     this.position("absolute", this.zindex);
22257     if(config.shadow){
22258         this.shadowOffset = config.shadowOffset || 4;
22259         this.shadow = new Roo.Shadow({
22260             offset : this.shadowOffset,
22261             mode : config.shadow
22262         });
22263     }else{
22264         this.shadowOffset = 0;
22265     }
22266     this.useShim = config.shim !== false && Roo.useShims;
22267     this.useDisplay = config.useDisplay;
22268     this.hide();
22269 };
22270
22271 var supr = Roo.Element.prototype;
22272
22273 // shims are shared among layer to keep from having 100 iframes
22274 var shims = [];
22275
22276 Roo.extend(Roo.Layer, Roo.Element, {
22277
22278     getZIndex : function(){
22279         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
22280     },
22281
22282     getShim : function(){
22283         if(!this.useShim){
22284             return null;
22285         }
22286         if(this.shim){
22287             return this.shim;
22288         }
22289         var shim = shims.shift();
22290         if(!shim){
22291             shim = this.createShim();
22292             shim.enableDisplayMode('block');
22293             shim.dom.style.display = 'none';
22294             shim.dom.style.visibility = 'visible';
22295         }
22296         var pn = this.dom.parentNode;
22297         if(shim.dom.parentNode != pn){
22298             pn.insertBefore(shim.dom, this.dom);
22299         }
22300         shim.setStyle('z-index', this.getZIndex()-2);
22301         this.shim = shim;
22302         return shim;
22303     },
22304
22305     hideShim : function(){
22306         if(this.shim){
22307             this.shim.setDisplayed(false);
22308             shims.push(this.shim);
22309             delete this.shim;
22310         }
22311     },
22312
22313     disableShadow : function(){
22314         if(this.shadow){
22315             this.shadowDisabled = true;
22316             this.shadow.hide();
22317             this.lastShadowOffset = this.shadowOffset;
22318             this.shadowOffset = 0;
22319         }
22320     },
22321
22322     enableShadow : function(show){
22323         if(this.shadow){
22324             this.shadowDisabled = false;
22325             this.shadowOffset = this.lastShadowOffset;
22326             delete this.lastShadowOffset;
22327             if(show){
22328                 this.sync(true);
22329             }
22330         }
22331     },
22332
22333     // private
22334     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
22335     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
22336     sync : function(doShow){
22337         var sw = this.shadow;
22338         if(!this.updating && this.isVisible() && (sw || this.useShim)){
22339             var sh = this.getShim();
22340
22341             var w = this.getWidth(),
22342                 h = this.getHeight();
22343
22344             var l = this.getLeft(true),
22345                 t = this.getTop(true);
22346
22347             if(sw && !this.shadowDisabled){
22348                 if(doShow && !sw.isVisible()){
22349                     sw.show(this);
22350                 }else{
22351                     sw.realign(l, t, w, h);
22352                 }
22353                 if(sh){
22354                     if(doShow){
22355                        sh.show();
22356                     }
22357                     // fit the shim behind the shadow, so it is shimmed too
22358                     var a = sw.adjusts, s = sh.dom.style;
22359                     s.left = (Math.min(l, l+a.l))+"px";
22360                     s.top = (Math.min(t, t+a.t))+"px";
22361                     s.width = (w+a.w)+"px";
22362                     s.height = (h+a.h)+"px";
22363                 }
22364             }else if(sh){
22365                 if(doShow){
22366                    sh.show();
22367                 }
22368                 sh.setSize(w, h);
22369                 sh.setLeftTop(l, t);
22370             }
22371             
22372         }
22373     },
22374
22375     // private
22376     destroy : function(){
22377         this.hideShim();
22378         if(this.shadow){
22379             this.shadow.hide();
22380         }
22381         this.removeAllListeners();
22382         var pn = this.dom.parentNode;
22383         if(pn){
22384             pn.removeChild(this.dom);
22385         }
22386         Roo.Element.uncache(this.id);
22387     },
22388
22389     remove : function(){
22390         this.destroy();
22391     },
22392
22393     // private
22394     beginUpdate : function(){
22395         this.updating = true;
22396     },
22397
22398     // private
22399     endUpdate : function(){
22400         this.updating = false;
22401         this.sync(true);
22402     },
22403
22404     // private
22405     hideUnders : function(negOffset){
22406         if(this.shadow){
22407             this.shadow.hide();
22408         }
22409         this.hideShim();
22410     },
22411
22412     // private
22413     constrainXY : function(){
22414         if(this.constrain){
22415             var vw = Roo.lib.Dom.getViewWidth(),
22416                 vh = Roo.lib.Dom.getViewHeight();
22417             var s = Roo.get(document).getScroll();
22418
22419             var xy = this.getXY();
22420             var x = xy[0], y = xy[1];   
22421             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
22422             // only move it if it needs it
22423             var moved = false;
22424             // first validate right/bottom
22425             if((x + w) > vw+s.left){
22426                 x = vw - w - this.shadowOffset;
22427                 moved = true;
22428             }
22429             if((y + h) > vh+s.top){
22430                 y = vh - h - this.shadowOffset;
22431                 moved = true;
22432             }
22433             // then make sure top/left isn't negative
22434             if(x < s.left){
22435                 x = s.left;
22436                 moved = true;
22437             }
22438             if(y < s.top){
22439                 y = s.top;
22440                 moved = true;
22441             }
22442             if(moved){
22443                 if(this.avoidY){
22444                     var ay = this.avoidY;
22445                     if(y <= ay && (y+h) >= ay){
22446                         y = ay-h-5;   
22447                     }
22448                 }
22449                 xy = [x, y];
22450                 this.storeXY(xy);
22451                 supr.setXY.call(this, xy);
22452                 this.sync();
22453             }
22454         }
22455     },
22456
22457     isVisible : function(){
22458         return this.visible;    
22459     },
22460
22461     // private
22462     showAction : function(){
22463         this.visible = true; // track visibility to prevent getStyle calls
22464         if(this.useDisplay === true){
22465             this.setDisplayed("");
22466         }else if(this.lastXY){
22467             supr.setXY.call(this, this.lastXY);
22468         }else if(this.lastLT){
22469             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
22470         }
22471     },
22472
22473     // private
22474     hideAction : function(){
22475         this.visible = false;
22476         if(this.useDisplay === true){
22477             this.setDisplayed(false);
22478         }else{
22479             this.setLeftTop(-10000,-10000);
22480         }
22481     },
22482
22483     // overridden Element method
22484     setVisible : function(v, a, d, c, e){
22485         if(v){
22486             this.showAction();
22487         }
22488         if(a && v){
22489             var cb = function(){
22490                 this.sync(true);
22491                 if(c){
22492                     c();
22493                 }
22494             }.createDelegate(this);
22495             supr.setVisible.call(this, true, true, d, cb, e);
22496         }else{
22497             if(!v){
22498                 this.hideUnders(true);
22499             }
22500             var cb = c;
22501             if(a){
22502                 cb = function(){
22503                     this.hideAction();
22504                     if(c){
22505                         c();
22506                     }
22507                 }.createDelegate(this);
22508             }
22509             supr.setVisible.call(this, v, a, d, cb, e);
22510             if(v){
22511                 this.sync(true);
22512             }else if(!a){
22513                 this.hideAction();
22514             }
22515         }
22516     },
22517
22518     storeXY : function(xy){
22519         delete this.lastLT;
22520         this.lastXY = xy;
22521     },
22522
22523     storeLeftTop : function(left, top){
22524         delete this.lastXY;
22525         this.lastLT = [left, top];
22526     },
22527
22528     // private
22529     beforeFx : function(){
22530         this.beforeAction();
22531         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
22532     },
22533
22534     // private
22535     afterFx : function(){
22536         Roo.Layer.superclass.afterFx.apply(this, arguments);
22537         this.sync(this.isVisible());
22538     },
22539
22540     // private
22541     beforeAction : function(){
22542         if(!this.updating && this.shadow){
22543             this.shadow.hide();
22544         }
22545     },
22546
22547     // overridden Element method
22548     setLeft : function(left){
22549         this.storeLeftTop(left, this.getTop(true));
22550         supr.setLeft.apply(this, arguments);
22551         this.sync();
22552     },
22553
22554     setTop : function(top){
22555         this.storeLeftTop(this.getLeft(true), top);
22556         supr.setTop.apply(this, arguments);
22557         this.sync();
22558     },
22559
22560     setLeftTop : function(left, top){
22561         this.storeLeftTop(left, top);
22562         supr.setLeftTop.apply(this, arguments);
22563         this.sync();
22564     },
22565
22566     setXY : function(xy, a, d, c, e){
22567         this.fixDisplay();
22568         this.beforeAction();
22569         this.storeXY(xy);
22570         var cb = this.createCB(c);
22571         supr.setXY.call(this, xy, a, d, cb, e);
22572         if(!a){
22573             cb();
22574         }
22575     },
22576
22577     // private
22578     createCB : function(c){
22579         var el = this;
22580         return function(){
22581             el.constrainXY();
22582             el.sync(true);
22583             if(c){
22584                 c();
22585             }
22586         };
22587     },
22588
22589     // overridden Element method
22590     setX : function(x, a, d, c, e){
22591         this.setXY([x, this.getY()], a, d, c, e);
22592     },
22593
22594     // overridden Element method
22595     setY : function(y, a, d, c, e){
22596         this.setXY([this.getX(), y], a, d, c, e);
22597     },
22598
22599     // overridden Element method
22600     setSize : function(w, h, a, d, c, e){
22601         this.beforeAction();
22602         var cb = this.createCB(c);
22603         supr.setSize.call(this, w, h, a, d, cb, e);
22604         if(!a){
22605             cb();
22606         }
22607     },
22608
22609     // overridden Element method
22610     setWidth : function(w, a, d, c, e){
22611         this.beforeAction();
22612         var cb = this.createCB(c);
22613         supr.setWidth.call(this, w, a, d, cb, e);
22614         if(!a){
22615             cb();
22616         }
22617     },
22618
22619     // overridden Element method
22620     setHeight : function(h, a, d, c, e){
22621         this.beforeAction();
22622         var cb = this.createCB(c);
22623         supr.setHeight.call(this, h, a, d, cb, e);
22624         if(!a){
22625             cb();
22626         }
22627     },
22628
22629     // overridden Element method
22630     setBounds : function(x, y, w, h, a, d, c, e){
22631         this.beforeAction();
22632         var cb = this.createCB(c);
22633         if(!a){
22634             this.storeXY([x, y]);
22635             supr.setXY.call(this, [x, y]);
22636             supr.setSize.call(this, w, h, a, d, cb, e);
22637             cb();
22638         }else{
22639             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
22640         }
22641         return this;
22642     },
22643     
22644     /**
22645      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
22646      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
22647      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
22648      * @param {Number} zindex The new z-index to set
22649      * @return {this} The Layer
22650      */
22651     setZIndex : function(zindex){
22652         this.zindex = zindex;
22653         this.setStyle("z-index", zindex + 2);
22654         if(this.shadow){
22655             this.shadow.setZIndex(zindex + 1);
22656         }
22657         if(this.shim){
22658             this.shim.setStyle("z-index", zindex);
22659         }
22660     }
22661 });
22662 })();/*
22663  * Based on:
22664  * Ext JS Library 1.1.1
22665  * Copyright(c) 2006-2007, Ext JS, LLC.
22666  *
22667  * Originally Released Under LGPL - original licence link has changed is not relivant.
22668  *
22669  * Fork - LGPL
22670  * <script type="text/javascript">
22671  */
22672
22673
22674 /**
22675  * @class Roo.Shadow
22676  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
22677  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
22678  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
22679  * @constructor
22680  * Create a new Shadow
22681  * @param {Object} config The config object
22682  */
22683 Roo.Shadow = function(config){
22684     Roo.apply(this, config);
22685     if(typeof this.mode != "string"){
22686         this.mode = this.defaultMode;
22687     }
22688     var o = this.offset, a = {h: 0};
22689     var rad = Math.floor(this.offset/2);
22690     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
22691         case "drop":
22692             a.w = 0;
22693             a.l = a.t = o;
22694             a.t -= 1;
22695             if(Roo.isIE){
22696                 a.l -= this.offset + rad;
22697                 a.t -= this.offset + rad;
22698                 a.w -= rad;
22699                 a.h -= rad;
22700                 a.t += 1;
22701             }
22702         break;
22703         case "sides":
22704             a.w = (o*2);
22705             a.l = -o;
22706             a.t = o-1;
22707             if(Roo.isIE){
22708                 a.l -= (this.offset - rad);
22709                 a.t -= this.offset + rad;
22710                 a.l += 1;
22711                 a.w -= (this.offset - rad)*2;
22712                 a.w -= rad + 1;
22713                 a.h -= 1;
22714             }
22715         break;
22716         case "frame":
22717             a.w = a.h = (o*2);
22718             a.l = a.t = -o;
22719             a.t += 1;
22720             a.h -= 2;
22721             if(Roo.isIE){
22722                 a.l -= (this.offset - rad);
22723                 a.t -= (this.offset - rad);
22724                 a.l += 1;
22725                 a.w -= (this.offset + rad + 1);
22726                 a.h -= (this.offset + rad);
22727                 a.h += 1;
22728             }
22729         break;
22730     };
22731
22732     this.adjusts = a;
22733 };
22734
22735 Roo.Shadow.prototype = {
22736     /**
22737      * @cfg {String} mode
22738      * The shadow display mode.  Supports the following options:<br />
22739      * sides: Shadow displays on both sides and bottom only<br />
22740      * frame: Shadow displays equally on all four sides<br />
22741      * drop: Traditional bottom-right drop shadow (default)
22742      */
22743     /**
22744      * @cfg {String} offset
22745      * The number of pixels to offset the shadow from the element (defaults to 4)
22746      */
22747     offset: 4,
22748
22749     // private
22750     defaultMode: "drop",
22751
22752     /**
22753      * Displays the shadow under the target element
22754      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
22755      */
22756     show : function(target){
22757         target = Roo.get(target);
22758         if(!this.el){
22759             this.el = Roo.Shadow.Pool.pull();
22760             if(this.el.dom.nextSibling != target.dom){
22761                 this.el.insertBefore(target);
22762             }
22763         }
22764         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
22765         if(Roo.isIE){
22766             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
22767         }
22768         this.realign(
22769             target.getLeft(true),
22770             target.getTop(true),
22771             target.getWidth(),
22772             target.getHeight()
22773         );
22774         this.el.dom.style.display = "block";
22775     },
22776
22777     /**
22778      * Returns true if the shadow is visible, else false
22779      */
22780     isVisible : function(){
22781         return this.el ? true : false;  
22782     },
22783
22784     /**
22785      * Direct alignment when values are already available. Show must be called at least once before
22786      * calling this method to ensure it is initialized.
22787      * @param {Number} left The target element left position
22788      * @param {Number} top The target element top position
22789      * @param {Number} width The target element width
22790      * @param {Number} height The target element height
22791      */
22792     realign : function(l, t, w, h){
22793         if(!this.el){
22794             return;
22795         }
22796         var a = this.adjusts, d = this.el.dom, s = d.style;
22797         var iea = 0;
22798         s.left = (l+a.l)+"px";
22799         s.top = (t+a.t)+"px";
22800         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
22801  
22802         if(s.width != sws || s.height != shs){
22803             s.width = sws;
22804             s.height = shs;
22805             if(!Roo.isIE){
22806                 var cn = d.childNodes;
22807                 var sww = Math.max(0, (sw-12))+"px";
22808                 cn[0].childNodes[1].style.width = sww;
22809                 cn[1].childNodes[1].style.width = sww;
22810                 cn[2].childNodes[1].style.width = sww;
22811                 cn[1].style.height = Math.max(0, (sh-12))+"px";
22812             }
22813         }
22814     },
22815
22816     /**
22817      * Hides this shadow
22818      */
22819     hide : function(){
22820         if(this.el){
22821             this.el.dom.style.display = "none";
22822             Roo.Shadow.Pool.push(this.el);
22823             delete this.el;
22824         }
22825     },
22826
22827     /**
22828      * Adjust the z-index of this shadow
22829      * @param {Number} zindex The new z-index
22830      */
22831     setZIndex : function(z){
22832         this.zIndex = z;
22833         if(this.el){
22834             this.el.setStyle("z-index", z);
22835         }
22836     }
22837 };
22838
22839 // Private utility class that manages the internal Shadow cache
22840 Roo.Shadow.Pool = function(){
22841     var p = [];
22842     var markup = Roo.isIE ?
22843                  '<div class="x-ie-shadow"></div>' :
22844                  '<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>';
22845     return {
22846         pull : function(){
22847             var sh = p.shift();
22848             if(!sh){
22849                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
22850                 sh.autoBoxAdjust = false;
22851             }
22852             return sh;
22853         },
22854
22855         push : function(sh){
22856             p.push(sh);
22857         }
22858     };
22859 }();/*
22860  * Based on:
22861  * Ext JS Library 1.1.1
22862  * Copyright(c) 2006-2007, Ext JS, LLC.
22863  *
22864  * Originally Released Under LGPL - original licence link has changed is not relivant.
22865  *
22866  * Fork - LGPL
22867  * <script type="text/javascript">
22868  */
22869
22870 /**
22871  * @class Roo.BoxComponent
22872  * @extends Roo.Component
22873  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
22874  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
22875  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
22876  * layout containers.
22877  * @constructor
22878  * @param {Roo.Element/String/Object} config The configuration options.
22879  */
22880 Roo.BoxComponent = function(config){
22881     Roo.Component.call(this, config);
22882     this.addEvents({
22883         /**
22884          * @event resize
22885          * Fires after the component is resized.
22886              * @param {Roo.Component} this
22887              * @param {Number} adjWidth The box-adjusted width that was set
22888              * @param {Number} adjHeight The box-adjusted height that was set
22889              * @param {Number} rawWidth The width that was originally specified
22890              * @param {Number} rawHeight The height that was originally specified
22891              */
22892         resize : true,
22893         /**
22894          * @event move
22895          * Fires after the component is moved.
22896              * @param {Roo.Component} this
22897              * @param {Number} x The new x position
22898              * @param {Number} y The new y position
22899              */
22900         move : true
22901     });
22902 };
22903
22904 Roo.extend(Roo.BoxComponent, Roo.Component, {
22905     // private, set in afterRender to signify that the component has been rendered
22906     boxReady : false,
22907     // private, used to defer height settings to subclasses
22908     deferHeight: false,
22909     /** @cfg {Number} width
22910      * width (optional) size of component
22911      */
22912      /** @cfg {Number} height
22913      * height (optional) size of component
22914      */
22915      
22916     /**
22917      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
22918      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
22919      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
22920      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
22921      * @return {Roo.BoxComponent} this
22922      */
22923     setSize : function(w, h){
22924         // support for standard size objects
22925         if(typeof w == 'object'){
22926             h = w.height;
22927             w = w.width;
22928         }
22929         // not rendered
22930         if(!this.boxReady){
22931             this.width = w;
22932             this.height = h;
22933             return this;
22934         }
22935
22936         // prevent recalcs when not needed
22937         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
22938             return this;
22939         }
22940         this.lastSize = {width: w, height: h};
22941
22942         var adj = this.adjustSize(w, h);
22943         var aw = adj.width, ah = adj.height;
22944         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
22945             var rz = this.getResizeEl();
22946             if(!this.deferHeight && aw !== undefined && ah !== undefined){
22947                 rz.setSize(aw, ah);
22948             }else if(!this.deferHeight && ah !== undefined){
22949                 rz.setHeight(ah);
22950             }else if(aw !== undefined){
22951                 rz.setWidth(aw);
22952             }
22953             this.onResize(aw, ah, w, h);
22954             this.fireEvent('resize', this, aw, ah, w, h);
22955         }
22956         return this;
22957     },
22958
22959     /**
22960      * Gets the current size of the component's underlying element.
22961      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
22962      */
22963     getSize : function(){
22964         return this.el.getSize();
22965     },
22966
22967     /**
22968      * Gets the current XY position of the component's underlying element.
22969      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
22970      * @return {Array} The XY position of the element (e.g., [100, 200])
22971      */
22972     getPosition : function(local){
22973         if(local === true){
22974             return [this.el.getLeft(true), this.el.getTop(true)];
22975         }
22976         return this.xy || this.el.getXY();
22977     },
22978
22979     /**
22980      * Gets the current box measurements of the component's underlying element.
22981      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
22982      * @returns {Object} box An object in the format {x, y, width, height}
22983      */
22984     getBox : function(local){
22985         var s = this.el.getSize();
22986         if(local){
22987             s.x = this.el.getLeft(true);
22988             s.y = this.el.getTop(true);
22989         }else{
22990             var xy = this.xy || this.el.getXY();
22991             s.x = xy[0];
22992             s.y = xy[1];
22993         }
22994         return s;
22995     },
22996
22997     /**
22998      * Sets the current box measurements of the component's underlying element.
22999      * @param {Object} box An object in the format {x, y, width, height}
23000      * @returns {Roo.BoxComponent} this
23001      */
23002     updateBox : function(box){
23003         this.setSize(box.width, box.height);
23004         this.setPagePosition(box.x, box.y);
23005         return this;
23006     },
23007
23008     // protected
23009     getResizeEl : function(){
23010         return this.resizeEl || this.el;
23011     },
23012
23013     // protected
23014     getPositionEl : function(){
23015         return this.positionEl || this.el;
23016     },
23017
23018     /**
23019      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
23020      * This method fires the move event.
23021      * @param {Number} left The new left
23022      * @param {Number} top The new top
23023      * @returns {Roo.BoxComponent} this
23024      */
23025     setPosition : function(x, y){
23026         this.x = x;
23027         this.y = y;
23028         if(!this.boxReady){
23029             return this;
23030         }
23031         var adj = this.adjustPosition(x, y);
23032         var ax = adj.x, ay = adj.y;
23033
23034         var el = this.getPositionEl();
23035         if(ax !== undefined || ay !== undefined){
23036             if(ax !== undefined && ay !== undefined){
23037                 el.setLeftTop(ax, ay);
23038             }else if(ax !== undefined){
23039                 el.setLeft(ax);
23040             }else if(ay !== undefined){
23041                 el.setTop(ay);
23042             }
23043             this.onPosition(ax, ay);
23044             this.fireEvent('move', this, ax, ay);
23045         }
23046         return this;
23047     },
23048
23049     /**
23050      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
23051      * This method fires the move event.
23052      * @param {Number} x The new x position
23053      * @param {Number} y The new y position
23054      * @returns {Roo.BoxComponent} this
23055      */
23056     setPagePosition : function(x, y){
23057         this.pageX = x;
23058         this.pageY = y;
23059         if(!this.boxReady){
23060             return;
23061         }
23062         if(x === undefined || y === undefined){ // cannot translate undefined points
23063             return;
23064         }
23065         var p = this.el.translatePoints(x, y);
23066         this.setPosition(p.left, p.top);
23067         return this;
23068     },
23069
23070     // private
23071     onRender : function(ct, position){
23072         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
23073         if(this.resizeEl){
23074             this.resizeEl = Roo.get(this.resizeEl);
23075         }
23076         if(this.positionEl){
23077             this.positionEl = Roo.get(this.positionEl);
23078         }
23079     },
23080
23081     // private
23082     afterRender : function(){
23083         Roo.BoxComponent.superclass.afterRender.call(this);
23084         this.boxReady = true;
23085         this.setSize(this.width, this.height);
23086         if(this.x || this.y){
23087             this.setPosition(this.x, this.y);
23088         }
23089         if(this.pageX || this.pageY){
23090             this.setPagePosition(this.pageX, this.pageY);
23091         }
23092     },
23093
23094     /**
23095      * Force the component's size to recalculate based on the underlying element's current height and width.
23096      * @returns {Roo.BoxComponent} this
23097      */
23098     syncSize : function(){
23099         delete this.lastSize;
23100         this.setSize(this.el.getWidth(), this.el.getHeight());
23101         return this;
23102     },
23103
23104     /**
23105      * Called after the component is resized, this method is empty by default but can be implemented by any
23106      * subclass that needs to perform custom logic after a resize occurs.
23107      * @param {Number} adjWidth The box-adjusted width that was set
23108      * @param {Number} adjHeight The box-adjusted height that was set
23109      * @param {Number} rawWidth The width that was originally specified
23110      * @param {Number} rawHeight The height that was originally specified
23111      */
23112     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
23113
23114     },
23115
23116     /**
23117      * Called after the component is moved, this method is empty by default but can be implemented by any
23118      * subclass that needs to perform custom logic after a move occurs.
23119      * @param {Number} x The new x position
23120      * @param {Number} y The new y position
23121      */
23122     onPosition : function(x, y){
23123
23124     },
23125
23126     // private
23127     adjustSize : function(w, h){
23128         if(this.autoWidth){
23129             w = 'auto';
23130         }
23131         if(this.autoHeight){
23132             h = 'auto';
23133         }
23134         return {width : w, height: h};
23135     },
23136
23137     // private
23138     adjustPosition : function(x, y){
23139         return {x : x, y: y};
23140     }
23141 });/*
23142  * Based on:
23143  * Ext JS Library 1.1.1
23144  * Copyright(c) 2006-2007, Ext JS, LLC.
23145  *
23146  * Originally Released Under LGPL - original licence link has changed is not relivant.
23147  *
23148  * Fork - LGPL
23149  * <script type="text/javascript">
23150  */
23151
23152
23153 /**
23154  * @class Roo.SplitBar
23155  * @extends Roo.util.Observable
23156  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
23157  * <br><br>
23158  * Usage:
23159  * <pre><code>
23160 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
23161                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
23162 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
23163 split.minSize = 100;
23164 split.maxSize = 600;
23165 split.animate = true;
23166 split.on('moved', splitterMoved);
23167 </code></pre>
23168  * @constructor
23169  * Create a new SplitBar
23170  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
23171  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
23172  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
23173  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
23174                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
23175                         position of the SplitBar).
23176  */
23177 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
23178     
23179     /** @private */
23180     this.el = Roo.get(dragElement, true);
23181     this.el.dom.unselectable = "on";
23182     /** @private */
23183     this.resizingEl = Roo.get(resizingElement, true);
23184
23185     /**
23186      * @private
23187      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
23188      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
23189      * @type Number
23190      */
23191     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
23192     
23193     /**
23194      * The minimum size of the resizing element. (Defaults to 0)
23195      * @type Number
23196      */
23197     this.minSize = 0;
23198     
23199     /**
23200      * The maximum size of the resizing element. (Defaults to 2000)
23201      * @type Number
23202      */
23203     this.maxSize = 2000;
23204     
23205     /**
23206      * Whether to animate the transition to the new size
23207      * @type Boolean
23208      */
23209     this.animate = false;
23210     
23211     /**
23212      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
23213      * @type Boolean
23214      */
23215     this.useShim = false;
23216     
23217     /** @private */
23218     this.shim = null;
23219     
23220     if(!existingProxy){
23221         /** @private */
23222         this.proxy = Roo.SplitBar.createProxy(this.orientation);
23223     }else{
23224         this.proxy = Roo.get(existingProxy).dom;
23225     }
23226     /** @private */
23227     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
23228     
23229     /** @private */
23230     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
23231     
23232     /** @private */
23233     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
23234     
23235     /** @private */
23236     this.dragSpecs = {};
23237     
23238     /**
23239      * @private The adapter to use to positon and resize elements
23240      */
23241     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
23242     this.adapter.init(this);
23243     
23244     if(this.orientation == Roo.SplitBar.HORIZONTAL){
23245         /** @private */
23246         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
23247         this.el.addClass("x-splitbar-h");
23248     }else{
23249         /** @private */
23250         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
23251         this.el.addClass("x-splitbar-v");
23252     }
23253     
23254     this.addEvents({
23255         /**
23256          * @event resize
23257          * Fires when the splitter is moved (alias for {@link #event-moved})
23258          * @param {Roo.SplitBar} this
23259          * @param {Number} newSize the new width or height
23260          */
23261         "resize" : true,
23262         /**
23263          * @event moved
23264          * Fires when the splitter is moved
23265          * @param {Roo.SplitBar} this
23266          * @param {Number} newSize the new width or height
23267          */
23268         "moved" : true,
23269         /**
23270          * @event beforeresize
23271          * Fires before the splitter is dragged
23272          * @param {Roo.SplitBar} this
23273          */
23274         "beforeresize" : true,
23275
23276         "beforeapply" : true
23277     });
23278
23279     Roo.util.Observable.call(this);
23280 };
23281
23282 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
23283     onStartProxyDrag : function(x, y){
23284         this.fireEvent("beforeresize", this);
23285         if(!this.overlay){
23286             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
23287             o.unselectable();
23288             o.enableDisplayMode("block");
23289             // all splitbars share the same overlay
23290             Roo.SplitBar.prototype.overlay = o;
23291         }
23292         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
23293         this.overlay.show();
23294         Roo.get(this.proxy).setDisplayed("block");
23295         var size = this.adapter.getElementSize(this);
23296         this.activeMinSize = this.getMinimumSize();;
23297         this.activeMaxSize = this.getMaximumSize();;
23298         var c1 = size - this.activeMinSize;
23299         var c2 = Math.max(this.activeMaxSize - size, 0);
23300         if(this.orientation == Roo.SplitBar.HORIZONTAL){
23301             this.dd.resetConstraints();
23302             this.dd.setXConstraint(
23303                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
23304                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
23305             );
23306             this.dd.setYConstraint(0, 0);
23307         }else{
23308             this.dd.resetConstraints();
23309             this.dd.setXConstraint(0, 0);
23310             this.dd.setYConstraint(
23311                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
23312                 this.placement == Roo.SplitBar.TOP ? c2 : c1
23313             );
23314          }
23315         this.dragSpecs.startSize = size;
23316         this.dragSpecs.startPoint = [x, y];
23317         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
23318     },
23319     
23320     /** 
23321      * @private Called after the drag operation by the DDProxy
23322      */
23323     onEndProxyDrag : function(e){
23324         Roo.get(this.proxy).setDisplayed(false);
23325         var endPoint = Roo.lib.Event.getXY(e);
23326         if(this.overlay){
23327             this.overlay.hide();
23328         }
23329         var newSize;
23330         if(this.orientation == Roo.SplitBar.HORIZONTAL){
23331             newSize = this.dragSpecs.startSize + 
23332                 (this.placement == Roo.SplitBar.LEFT ?
23333                     endPoint[0] - this.dragSpecs.startPoint[0] :
23334                     this.dragSpecs.startPoint[0] - endPoint[0]
23335                 );
23336         }else{
23337             newSize = this.dragSpecs.startSize + 
23338                 (this.placement == Roo.SplitBar.TOP ?
23339                     endPoint[1] - this.dragSpecs.startPoint[1] :
23340                     this.dragSpecs.startPoint[1] - endPoint[1]
23341                 );
23342         }
23343         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
23344         if(newSize != this.dragSpecs.startSize){
23345             if(this.fireEvent('beforeapply', this, newSize) !== false){
23346                 this.adapter.setElementSize(this, newSize);
23347                 this.fireEvent("moved", this, newSize);
23348                 this.fireEvent("resize", this, newSize);
23349             }
23350         }
23351     },
23352     
23353     /**
23354      * Get the adapter this SplitBar uses
23355      * @return The adapter object
23356      */
23357     getAdapter : function(){
23358         return this.adapter;
23359     },
23360     
23361     /**
23362      * Set the adapter this SplitBar uses
23363      * @param {Object} adapter A SplitBar adapter object
23364      */
23365     setAdapter : function(adapter){
23366         this.adapter = adapter;
23367         this.adapter.init(this);
23368     },
23369     
23370     /**
23371      * Gets the minimum size for the resizing element
23372      * @return {Number} The minimum size
23373      */
23374     getMinimumSize : function(){
23375         return this.minSize;
23376     },
23377     
23378     /**
23379      * Sets the minimum size for the resizing element
23380      * @param {Number} minSize The minimum size
23381      */
23382     setMinimumSize : function(minSize){
23383         this.minSize = minSize;
23384     },
23385     
23386     /**
23387      * Gets the maximum size for the resizing element
23388      * @return {Number} The maximum size
23389      */
23390     getMaximumSize : function(){
23391         return this.maxSize;
23392     },
23393     
23394     /**
23395      * Sets the maximum size for the resizing element
23396      * @param {Number} maxSize The maximum size
23397      */
23398     setMaximumSize : function(maxSize){
23399         this.maxSize = maxSize;
23400     },
23401     
23402     /**
23403      * Sets the initialize size for the resizing element
23404      * @param {Number} size The initial size
23405      */
23406     setCurrentSize : function(size){
23407         var oldAnimate = this.animate;
23408         this.animate = false;
23409         this.adapter.setElementSize(this, size);
23410         this.animate = oldAnimate;
23411     },
23412     
23413     /**
23414      * Destroy this splitbar. 
23415      * @param {Boolean} removeEl True to remove the element
23416      */
23417     destroy : function(removeEl){
23418         if(this.shim){
23419             this.shim.remove();
23420         }
23421         this.dd.unreg();
23422         this.proxy.parentNode.removeChild(this.proxy);
23423         if(removeEl){
23424             this.el.remove();
23425         }
23426     }
23427 });
23428
23429 /**
23430  * @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.
23431  */
23432 Roo.SplitBar.createProxy = function(dir){
23433     var proxy = new Roo.Element(document.createElement("div"));
23434     proxy.unselectable();
23435     var cls = 'x-splitbar-proxy';
23436     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
23437     document.body.appendChild(proxy.dom);
23438     return proxy.dom;
23439 };
23440
23441 /** 
23442  * @class Roo.SplitBar.BasicLayoutAdapter
23443  * Default Adapter. It assumes the splitter and resizing element are not positioned
23444  * elements and only gets/sets the width of the element. Generally used for table based layouts.
23445  */
23446 Roo.SplitBar.BasicLayoutAdapter = function(){
23447 };
23448
23449 Roo.SplitBar.BasicLayoutAdapter.prototype = {
23450     // do nothing for now
23451     init : function(s){
23452     
23453     },
23454     /**
23455      * Called before drag operations to get the current size of the resizing element. 
23456      * @param {Roo.SplitBar} s The SplitBar using this adapter
23457      */
23458      getElementSize : function(s){
23459         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23460             return s.resizingEl.getWidth();
23461         }else{
23462             return s.resizingEl.getHeight();
23463         }
23464     },
23465     
23466     /**
23467      * Called after drag operations to set the size of the resizing element.
23468      * @param {Roo.SplitBar} s The SplitBar using this adapter
23469      * @param {Number} newSize The new size to set
23470      * @param {Function} onComplete A function to be invoked when resizing is complete
23471      */
23472     setElementSize : function(s, newSize, onComplete){
23473         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23474             if(!s.animate){
23475                 s.resizingEl.setWidth(newSize);
23476                 if(onComplete){
23477                     onComplete(s, newSize);
23478                 }
23479             }else{
23480                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
23481             }
23482         }else{
23483             
23484             if(!s.animate){
23485                 s.resizingEl.setHeight(newSize);
23486                 if(onComplete){
23487                     onComplete(s, newSize);
23488                 }
23489             }else{
23490                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
23491             }
23492         }
23493     }
23494 };
23495
23496 /** 
23497  *@class Roo.SplitBar.AbsoluteLayoutAdapter
23498  * @extends Roo.SplitBar.BasicLayoutAdapter
23499  * Adapter that  moves the splitter element to align with the resized sizing element. 
23500  * Used with an absolute positioned SplitBar.
23501  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
23502  * document.body, make sure you assign an id to the body element.
23503  */
23504 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
23505     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
23506     this.container = Roo.get(container);
23507 };
23508
23509 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
23510     init : function(s){
23511         this.basic.init(s);
23512     },
23513     
23514     getElementSize : function(s){
23515         return this.basic.getElementSize(s);
23516     },
23517     
23518     setElementSize : function(s, newSize, onComplete){
23519         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
23520     },
23521     
23522     moveSplitter : function(s){
23523         var yes = Roo.SplitBar;
23524         switch(s.placement){
23525             case yes.LEFT:
23526                 s.el.setX(s.resizingEl.getRight());
23527                 break;
23528             case yes.RIGHT:
23529                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
23530                 break;
23531             case yes.TOP:
23532                 s.el.setY(s.resizingEl.getBottom());
23533                 break;
23534             case yes.BOTTOM:
23535                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
23536                 break;
23537         }
23538     }
23539 };
23540
23541 /**
23542  * Orientation constant - Create a vertical SplitBar
23543  * @static
23544  * @type Number
23545  */
23546 Roo.SplitBar.VERTICAL = 1;
23547
23548 /**
23549  * Orientation constant - Create a horizontal SplitBar
23550  * @static
23551  * @type Number
23552  */
23553 Roo.SplitBar.HORIZONTAL = 2;
23554
23555 /**
23556  * Placement constant - The resizing element is to the left of the splitter element
23557  * @static
23558  * @type Number
23559  */
23560 Roo.SplitBar.LEFT = 1;
23561
23562 /**
23563  * Placement constant - The resizing element is to the right of the splitter element
23564  * @static
23565  * @type Number
23566  */
23567 Roo.SplitBar.RIGHT = 2;
23568
23569 /**
23570  * Placement constant - The resizing element is positioned above the splitter element
23571  * @static
23572  * @type Number
23573  */
23574 Roo.SplitBar.TOP = 3;
23575
23576 /**
23577  * Placement constant - The resizing element is positioned under splitter element
23578  * @static
23579  * @type Number
23580  */
23581 Roo.SplitBar.BOTTOM = 4;
23582 /*
23583  * Based on:
23584  * Ext JS Library 1.1.1
23585  * Copyright(c) 2006-2007, Ext JS, LLC.
23586  *
23587  * Originally Released Under LGPL - original licence link has changed is not relivant.
23588  *
23589  * Fork - LGPL
23590  * <script type="text/javascript">
23591  */
23592
23593 /**
23594  * @class Roo.View
23595  * @extends Roo.util.Observable
23596  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
23597  * This class also supports single and multi selection modes. <br>
23598  * Create a data model bound view:
23599  <pre><code>
23600  var store = new Roo.data.Store(...);
23601
23602  var view = new Roo.View({
23603     el : "my-element",
23604     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
23605  
23606     singleSelect: true,
23607     selectedClass: "ydataview-selected",
23608     store: store
23609  });
23610
23611  // listen for node click?
23612  view.on("click", function(vw, index, node, e){
23613  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
23614  });
23615
23616  // load XML data
23617  dataModel.load("foobar.xml");
23618  </code></pre>
23619  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
23620  * <br><br>
23621  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
23622  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
23623  * 
23624  * Note: old style constructor is still suported (container, template, config)
23625  * 
23626  * @constructor
23627  * Create a new View
23628  * @param {Object} config The config object
23629  * 
23630  */
23631 Roo.View = function(config, depreciated_tpl, depreciated_config){
23632     
23633     if (typeof(depreciated_tpl) == 'undefined') {
23634         // new way.. - universal constructor.
23635         Roo.apply(this, config);
23636         this.el  = Roo.get(this.el);
23637     } else {
23638         // old format..
23639         this.el  = Roo.get(config);
23640         this.tpl = depreciated_tpl;
23641         Roo.apply(this, depreciated_config);
23642     }
23643      
23644     
23645     if(typeof(this.tpl) == "string"){
23646         this.tpl = new Roo.Template(this.tpl);
23647     } else {
23648         // support xtype ctors..
23649         this.tpl = new Roo.factory(this.tpl, Roo);
23650     }
23651     
23652     
23653     this.tpl.compile();
23654    
23655
23656      
23657     /** @private */
23658     this.addEvents({
23659         /**
23660          * @event beforeclick
23661          * Fires before a click is processed. Returns false to cancel the default action.
23662          * @param {Roo.View} this
23663          * @param {Number} index The index of the target node
23664          * @param {HTMLElement} node The target node
23665          * @param {Roo.EventObject} e The raw event object
23666          */
23667             "beforeclick" : true,
23668         /**
23669          * @event click
23670          * Fires when a template node is clicked.
23671          * @param {Roo.View} this
23672          * @param {Number} index The index of the target node
23673          * @param {HTMLElement} node The target node
23674          * @param {Roo.EventObject} e The raw event object
23675          */
23676             "click" : true,
23677         /**
23678          * @event dblclick
23679          * Fires when a template node is double clicked.
23680          * @param {Roo.View} this
23681          * @param {Number} index The index of the target node
23682          * @param {HTMLElement} node The target node
23683          * @param {Roo.EventObject} e The raw event object
23684          */
23685             "dblclick" : true,
23686         /**
23687          * @event contextmenu
23688          * Fires when a template node is right clicked.
23689          * @param {Roo.View} this
23690          * @param {Number} index The index of the target node
23691          * @param {HTMLElement} node The target node
23692          * @param {Roo.EventObject} e The raw event object
23693          */
23694             "contextmenu" : true,
23695         /**
23696          * @event selectionchange
23697          * Fires when the selected nodes change.
23698          * @param {Roo.View} this
23699          * @param {Array} selections Array of the selected nodes
23700          */
23701             "selectionchange" : true,
23702     
23703         /**
23704          * @event beforeselect
23705          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
23706          * @param {Roo.View} this
23707          * @param {HTMLElement} node The node to be selected
23708          * @param {Array} selections Array of currently selected nodes
23709          */
23710             "beforeselect" : true,
23711         /**
23712          * @event preparedata
23713          * Fires on every row to render, to allow you to change the data.
23714          * @param {Roo.View} this
23715          * @param {Object} data to be rendered (change this)
23716          */
23717           "preparedata" : true
23718         });
23719
23720     this.el.on({
23721         "click": this.onClick,
23722         "dblclick": this.onDblClick,
23723         "contextmenu": this.onContextMenu,
23724         scope:this
23725     });
23726
23727     this.selections = [];
23728     this.nodes = [];
23729     this.cmp = new Roo.CompositeElementLite([]);
23730     if(this.store){
23731         this.store = Roo.factory(this.store, Roo.data);
23732         this.setStore(this.store, true);
23733     }
23734     Roo.View.superclass.constructor.call(this);
23735 };
23736
23737 Roo.extend(Roo.View, Roo.util.Observable, {
23738     
23739      /**
23740      * @cfg {Roo.data.Store} store Data store to load data from.
23741      */
23742     store : false,
23743     
23744     /**
23745      * @cfg {String|Roo.Element} el The container element.
23746      */
23747     el : '',
23748     
23749     /**
23750      * @cfg {String|Roo.Template} tpl The template used by this View 
23751      */
23752     tpl : false,
23753     
23754     /**
23755      * @cfg {String} selectedClass The css class to add to selected nodes
23756      */
23757     selectedClass : "x-view-selected",
23758      /**
23759      * @cfg {String} emptyText The empty text to show when nothing is loaded.
23760      */
23761     emptyText : "",
23762     /**
23763      * @cfg {Boolean} multiSelect Allow multiple selection
23764      */
23765     multiSelect : false,
23766     /**
23767      * @cfg {Boolean} singleSelect Allow single selection
23768      */
23769     singleSelect:  false,
23770     
23771     /**
23772      * @cfg {Boolean} toggleSelect - selecting 
23773      */
23774     toggleSelect : false,
23775     
23776     /**
23777      * Returns the element this view is bound to.
23778      * @return {Roo.Element}
23779      */
23780     getEl : function(){
23781         return this.el;
23782     },
23783
23784     /**
23785      * Refreshes the view.
23786      */
23787     refresh : function(){
23788         var t = this.tpl;
23789         this.clearSelections();
23790         this.el.update("");
23791         var html = [];
23792         var records = this.store.getRange();
23793         if(records.length < 1){
23794             this.el.update(this.emptyText);
23795             return;
23796         }
23797         for(var i = 0, len = records.length; i < len; i++){
23798             var data = this.prepareData(records[i].data, i, records[i]);
23799             this.fireEvent("preparedata", this, data, i, records[i]);
23800             html[html.length] = t.apply(data);
23801         }
23802         this.el.update(html.join(""));
23803         this.nodes = this.el.dom.childNodes;
23804         this.updateIndexes(0);
23805     },
23806
23807     /**
23808      * Function to override to reformat the data that is sent to
23809      * the template for each node.
23810      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
23811      * a JSON object for an UpdateManager bound view).
23812      */
23813     prepareData : function(data){
23814         return data;
23815     },
23816
23817     onUpdate : function(ds, record){
23818         this.clearSelections();
23819         var index = this.store.indexOf(record);
23820         var n = this.nodes[index];
23821         this.tpl.insertBefore(n, this.prepareData(record.data));
23822         n.parentNode.removeChild(n);
23823         this.updateIndexes(index, index);
23824     },
23825
23826     onAdd : function(ds, records, index){
23827         this.clearSelections();
23828         if(this.nodes.length == 0){
23829             this.refresh();
23830             return;
23831         }
23832         var n = this.nodes[index];
23833         for(var i = 0, len = records.length; i < len; i++){
23834             var d = this.prepareData(records[i].data);
23835             if(n){
23836                 this.tpl.insertBefore(n, d);
23837             }else{
23838                 this.tpl.append(this.el, d);
23839             }
23840         }
23841         this.updateIndexes(index);
23842     },
23843
23844     onRemove : function(ds, record, index){
23845         this.clearSelections();
23846         this.el.dom.removeChild(this.nodes[index]);
23847         this.updateIndexes(index);
23848     },
23849
23850     /**
23851      * Refresh an individual node.
23852      * @param {Number} index
23853      */
23854     refreshNode : function(index){
23855         this.onUpdate(this.store, this.store.getAt(index));
23856     },
23857
23858     updateIndexes : function(startIndex, endIndex){
23859         var ns = this.nodes;
23860         startIndex = startIndex || 0;
23861         endIndex = endIndex || ns.length - 1;
23862         for(var i = startIndex; i <= endIndex; i++){
23863             ns[i].nodeIndex = i;
23864         }
23865     },
23866
23867     /**
23868      * Changes the data store this view uses and refresh the view.
23869      * @param {Store} store
23870      */
23871     setStore : function(store, initial){
23872         if(!initial && this.store){
23873             this.store.un("datachanged", this.refresh);
23874             this.store.un("add", this.onAdd);
23875             this.store.un("remove", this.onRemove);
23876             this.store.un("update", this.onUpdate);
23877             this.store.un("clear", this.refresh);
23878         }
23879         if(store){
23880           
23881             store.on("datachanged", this.refresh, this);
23882             store.on("add", this.onAdd, this);
23883             store.on("remove", this.onRemove, this);
23884             store.on("update", this.onUpdate, this);
23885             store.on("clear", this.refresh, this);
23886         }
23887         
23888         if(store){
23889             this.refresh();
23890         }
23891     },
23892
23893     /**
23894      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
23895      * @param {HTMLElement} node
23896      * @return {HTMLElement} The template node
23897      */
23898     findItemFromChild : function(node){
23899         var el = this.el.dom;
23900         if(!node || node.parentNode == el){
23901                     return node;
23902             }
23903             var p = node.parentNode;
23904             while(p && p != el){
23905             if(p.parentNode == el){
23906                 return p;
23907             }
23908             p = p.parentNode;
23909         }
23910             return null;
23911     },
23912
23913     /** @ignore */
23914     onClick : function(e){
23915         var item = this.findItemFromChild(e.getTarget());
23916         if(item){
23917             var index = this.indexOf(item);
23918             if(this.onItemClick(item, index, e) !== false){
23919                 this.fireEvent("click", this, index, item, e);
23920             }
23921         }else{
23922             this.clearSelections();
23923         }
23924     },
23925
23926     /** @ignore */
23927     onContextMenu : function(e){
23928         var item = this.findItemFromChild(e.getTarget());
23929         if(item){
23930             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
23931         }
23932     },
23933
23934     /** @ignore */
23935     onDblClick : function(e){
23936         var item = this.findItemFromChild(e.getTarget());
23937         if(item){
23938             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
23939         }
23940     },
23941
23942     onItemClick : function(item, index, e)
23943     {
23944         if(this.fireEvent("beforeclick", this, index, item, e) === false){
23945             return false;
23946         }
23947         if (this.toggleSelect) {
23948             var m = this.isSelected(item) ? 'unselect' : 'select';
23949             Roo.log(m);
23950             var _t = this;
23951             _t[m](item, true, false);
23952             return true;
23953         }
23954         if(this.multiSelect || this.singleSelect){
23955             if(this.multiSelect && e.shiftKey && this.lastSelection){
23956                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
23957             }else{
23958                 this.select(item, this.multiSelect && e.ctrlKey);
23959                 this.lastSelection = item;
23960             }
23961             e.preventDefault();
23962         }
23963         return true;
23964     },
23965
23966     /**
23967      * Get the number of selected nodes.
23968      * @return {Number}
23969      */
23970     getSelectionCount : function(){
23971         return this.selections.length;
23972     },
23973
23974     /**
23975      * Get the currently selected nodes.
23976      * @return {Array} An array of HTMLElements
23977      */
23978     getSelectedNodes : function(){
23979         return this.selections;
23980     },
23981
23982     /**
23983      * Get the indexes of the selected nodes.
23984      * @return {Array}
23985      */
23986     getSelectedIndexes : function(){
23987         var indexes = [], s = this.selections;
23988         for(var i = 0, len = s.length; i < len; i++){
23989             indexes.push(s[i].nodeIndex);
23990         }
23991         return indexes;
23992     },
23993
23994     /**
23995      * Clear all selections
23996      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
23997      */
23998     clearSelections : function(suppressEvent){
23999         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
24000             this.cmp.elements = this.selections;
24001             this.cmp.removeClass(this.selectedClass);
24002             this.selections = [];
24003             if(!suppressEvent){
24004                 this.fireEvent("selectionchange", this, this.selections);
24005             }
24006         }
24007     },
24008
24009     /**
24010      * Returns true if the passed node is selected
24011      * @param {HTMLElement/Number} node The node or node index
24012      * @return {Boolean}
24013      */
24014     isSelected : function(node){
24015         var s = this.selections;
24016         if(s.length < 1){
24017             return false;
24018         }
24019         node = this.getNode(node);
24020         return s.indexOf(node) !== -1;
24021     },
24022
24023     /**
24024      * Selects nodes.
24025      * @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
24026      * @param {Boolean} keepExisting (optional) true to keep existing selections
24027      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
24028      */
24029     select : function(nodeInfo, keepExisting, suppressEvent){
24030         if(nodeInfo instanceof Array){
24031             if(!keepExisting){
24032                 this.clearSelections(true);
24033             }
24034             for(var i = 0, len = nodeInfo.length; i < len; i++){
24035                 this.select(nodeInfo[i], true, true);
24036             }
24037             return;
24038         } 
24039         var node = this.getNode(nodeInfo);
24040         if(!node || this.isSelected(node)){
24041             return; // already selected.
24042         }
24043         if(!keepExisting){
24044             this.clearSelections(true);
24045         }
24046         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
24047             Roo.fly(node).addClass(this.selectedClass);
24048             this.selections.push(node);
24049             if(!suppressEvent){
24050                 this.fireEvent("selectionchange", this, this.selections);
24051             }
24052         }
24053         
24054         
24055     },
24056       /**
24057      * Unselects nodes.
24058      * @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
24059      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
24060      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
24061      */
24062     unselect : function(nodeInfo, keepExisting, suppressEvent)
24063     {
24064         if(nodeInfo instanceof Array){
24065             Roo.each(this.selections, function(s) {
24066                 this.unselect(s, nodeInfo);
24067             }, this);
24068             return;
24069         }
24070         var node = this.getNode(nodeInfo);
24071         if(!node || !this.isSelected(node)){
24072             Roo.log("not selected");
24073             return; // not selected.
24074         }
24075         // fireevent???
24076         var ns = [];
24077         Roo.each(this.selections, function(s) {
24078             if (s == node ) {
24079                 Roo.fly(node).removeClass(this.selectedClass);
24080
24081                 return;
24082             }
24083             ns.push(s);
24084         },this);
24085         
24086         this.selections= ns;
24087         this.fireEvent("selectionchange", this, this.selections);
24088     },
24089
24090     /**
24091      * Gets a template node.
24092      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
24093      * @return {HTMLElement} The node or null if it wasn't found
24094      */
24095     getNode : function(nodeInfo){
24096         if(typeof nodeInfo == "string"){
24097             return document.getElementById(nodeInfo);
24098         }else if(typeof nodeInfo == "number"){
24099             return this.nodes[nodeInfo];
24100         }
24101         return nodeInfo;
24102     },
24103
24104     /**
24105      * Gets a range template nodes.
24106      * @param {Number} startIndex
24107      * @param {Number} endIndex
24108      * @return {Array} An array of nodes
24109      */
24110     getNodes : function(start, end){
24111         var ns = this.nodes;
24112         start = start || 0;
24113         end = typeof end == "undefined" ? ns.length - 1 : end;
24114         var nodes = [];
24115         if(start <= end){
24116             for(var i = start; i <= end; i++){
24117                 nodes.push(ns[i]);
24118             }
24119         } else{
24120             for(var i = start; i >= end; i--){
24121                 nodes.push(ns[i]);
24122             }
24123         }
24124         return nodes;
24125     },
24126
24127     /**
24128      * Finds the index of the passed node
24129      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
24130      * @return {Number} The index of the node or -1
24131      */
24132     indexOf : function(node){
24133         node = this.getNode(node);
24134         if(typeof node.nodeIndex == "number"){
24135             return node.nodeIndex;
24136         }
24137         var ns = this.nodes;
24138         for(var i = 0, len = ns.length; i < len; i++){
24139             if(ns[i] == node){
24140                 return i;
24141             }
24142         }
24143         return -1;
24144     }
24145 });
24146 /*
24147  * Based on:
24148  * Ext JS Library 1.1.1
24149  * Copyright(c) 2006-2007, Ext JS, LLC.
24150  *
24151  * Originally Released Under LGPL - original licence link has changed is not relivant.
24152  *
24153  * Fork - LGPL
24154  * <script type="text/javascript">
24155  */
24156
24157 /**
24158  * @class Roo.JsonView
24159  * @extends Roo.View
24160  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
24161 <pre><code>
24162 var view = new Roo.JsonView({
24163     container: "my-element",
24164     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
24165     multiSelect: true, 
24166     jsonRoot: "data" 
24167 });
24168
24169 // listen for node click?
24170 view.on("click", function(vw, index, node, e){
24171     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
24172 });
24173
24174 // direct load of JSON data
24175 view.load("foobar.php");
24176
24177 // Example from my blog list
24178 var tpl = new Roo.Template(
24179     '&lt;div class="entry"&gt;' +
24180     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
24181     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
24182     "&lt;/div&gt;&lt;hr /&gt;"
24183 );
24184
24185 var moreView = new Roo.JsonView({
24186     container :  "entry-list", 
24187     template : tpl,
24188     jsonRoot: "posts"
24189 });
24190 moreView.on("beforerender", this.sortEntries, this);
24191 moreView.load({
24192     url: "/blog/get-posts.php",
24193     params: "allposts=true",
24194     text: "Loading Blog Entries..."
24195 });
24196 </code></pre>
24197
24198 * Note: old code is supported with arguments : (container, template, config)
24199
24200
24201  * @constructor
24202  * Create a new JsonView
24203  * 
24204  * @param {Object} config The config object
24205  * 
24206  */
24207 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
24208     
24209     
24210     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
24211
24212     var um = this.el.getUpdateManager();
24213     um.setRenderer(this);
24214     um.on("update", this.onLoad, this);
24215     um.on("failure", this.onLoadException, this);
24216
24217     /**
24218      * @event beforerender
24219      * Fires before rendering of the downloaded JSON data.
24220      * @param {Roo.JsonView} this
24221      * @param {Object} data The JSON data loaded
24222      */
24223     /**
24224      * @event load
24225      * Fires when data is loaded.
24226      * @param {Roo.JsonView} this
24227      * @param {Object} data The JSON data loaded
24228      * @param {Object} response The raw Connect response object
24229      */
24230     /**
24231      * @event loadexception
24232      * Fires when loading fails.
24233      * @param {Roo.JsonView} this
24234      * @param {Object} response The raw Connect response object
24235      */
24236     this.addEvents({
24237         'beforerender' : true,
24238         'load' : true,
24239         'loadexception' : true
24240     });
24241 };
24242 Roo.extend(Roo.JsonView, Roo.View, {
24243     /**
24244      * @type {String} The root property in the loaded JSON object that contains the data
24245      */
24246     jsonRoot : "",
24247
24248     /**
24249      * Refreshes the view.
24250      */
24251     refresh : function(){
24252         this.clearSelections();
24253         this.el.update("");
24254         var html = [];
24255         var o = this.jsonData;
24256         if(o && o.length > 0){
24257             for(var i = 0, len = o.length; i < len; i++){
24258                 var data = this.prepareData(o[i], i, o);
24259                 html[html.length] = this.tpl.apply(data);
24260             }
24261         }else{
24262             html.push(this.emptyText);
24263         }
24264         this.el.update(html.join(""));
24265         this.nodes = this.el.dom.childNodes;
24266         this.updateIndexes(0);
24267     },
24268
24269     /**
24270      * 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.
24271      * @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:
24272      <pre><code>
24273      view.load({
24274          url: "your-url.php",
24275          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
24276          callback: yourFunction,
24277          scope: yourObject, //(optional scope)
24278          discardUrl: false,
24279          nocache: false,
24280          text: "Loading...",
24281          timeout: 30,
24282          scripts: false
24283      });
24284      </code></pre>
24285      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
24286      * 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.
24287      * @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}
24288      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
24289      * @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.
24290      */
24291     load : function(){
24292         var um = this.el.getUpdateManager();
24293         um.update.apply(um, arguments);
24294     },
24295
24296     render : function(el, response){
24297         this.clearSelections();
24298         this.el.update("");
24299         var o;
24300         try{
24301             o = Roo.util.JSON.decode(response.responseText);
24302             if(this.jsonRoot){
24303                 
24304                 o = o[this.jsonRoot];
24305             }
24306         } catch(e){
24307         }
24308         /**
24309          * The current JSON data or null
24310          */
24311         this.jsonData = o;
24312         this.beforeRender();
24313         this.refresh();
24314     },
24315
24316 /**
24317  * Get the number of records in the current JSON dataset
24318  * @return {Number}
24319  */
24320     getCount : function(){
24321         return this.jsonData ? this.jsonData.length : 0;
24322     },
24323
24324 /**
24325  * Returns the JSON object for the specified node(s)
24326  * @param {HTMLElement/Array} node The node or an array of nodes
24327  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
24328  * you get the JSON object for the node
24329  */
24330     getNodeData : function(node){
24331         if(node instanceof Array){
24332             var data = [];
24333             for(var i = 0, len = node.length; i < len; i++){
24334                 data.push(this.getNodeData(node[i]));
24335             }
24336             return data;
24337         }
24338         return this.jsonData[this.indexOf(node)] || null;
24339     },
24340
24341     beforeRender : function(){
24342         this.snapshot = this.jsonData;
24343         if(this.sortInfo){
24344             this.sort.apply(this, this.sortInfo);
24345         }
24346         this.fireEvent("beforerender", this, this.jsonData);
24347     },
24348
24349     onLoad : function(el, o){
24350         this.fireEvent("load", this, this.jsonData, o);
24351     },
24352
24353     onLoadException : function(el, o){
24354         this.fireEvent("loadexception", this, o);
24355     },
24356
24357 /**
24358  * Filter the data by a specific property.
24359  * @param {String} property A property on your JSON objects
24360  * @param {String/RegExp} value Either string that the property values
24361  * should start with, or a RegExp to test against the property
24362  */
24363     filter : function(property, value){
24364         if(this.jsonData){
24365             var data = [];
24366             var ss = this.snapshot;
24367             if(typeof value == "string"){
24368                 var vlen = value.length;
24369                 if(vlen == 0){
24370                     this.clearFilter();
24371                     return;
24372                 }
24373                 value = value.toLowerCase();
24374                 for(var i = 0, len = ss.length; i < len; i++){
24375                     var o = ss[i];
24376                     if(o[property].substr(0, vlen).toLowerCase() == value){
24377                         data.push(o);
24378                     }
24379                 }
24380             } else if(value.exec){ // regex?
24381                 for(var i = 0, len = ss.length; i < len; i++){
24382                     var o = ss[i];
24383                     if(value.test(o[property])){
24384                         data.push(o);
24385                     }
24386                 }
24387             } else{
24388                 return;
24389             }
24390             this.jsonData = data;
24391             this.refresh();
24392         }
24393     },
24394
24395 /**
24396  * Filter by a function. The passed function will be called with each
24397  * object in the current dataset. If the function returns true the value is kept,
24398  * otherwise it is filtered.
24399  * @param {Function} fn
24400  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
24401  */
24402     filterBy : function(fn, scope){
24403         if(this.jsonData){
24404             var data = [];
24405             var ss = this.snapshot;
24406             for(var i = 0, len = ss.length; i < len; i++){
24407                 var o = ss[i];
24408                 if(fn.call(scope || this, o)){
24409                     data.push(o);
24410                 }
24411             }
24412             this.jsonData = data;
24413             this.refresh();
24414         }
24415     },
24416
24417 /**
24418  * Clears the current filter.
24419  */
24420     clearFilter : function(){
24421         if(this.snapshot && this.jsonData != this.snapshot){
24422             this.jsonData = this.snapshot;
24423             this.refresh();
24424         }
24425     },
24426
24427
24428 /**
24429  * Sorts the data for this view and refreshes it.
24430  * @param {String} property A property on your JSON objects to sort on
24431  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
24432  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
24433  */
24434     sort : function(property, dir, sortType){
24435         this.sortInfo = Array.prototype.slice.call(arguments, 0);
24436         if(this.jsonData){
24437             var p = property;
24438             var dsc = dir && dir.toLowerCase() == "desc";
24439             var f = function(o1, o2){
24440                 var v1 = sortType ? sortType(o1[p]) : o1[p];
24441                 var v2 = sortType ? sortType(o2[p]) : o2[p];
24442                 ;
24443                 if(v1 < v2){
24444                     return dsc ? +1 : -1;
24445                 } else if(v1 > v2){
24446                     return dsc ? -1 : +1;
24447                 } else{
24448                     return 0;
24449                 }
24450             };
24451             this.jsonData.sort(f);
24452             this.refresh();
24453             if(this.jsonData != this.snapshot){
24454                 this.snapshot.sort(f);
24455             }
24456         }
24457     }
24458 });/*
24459  * Based on:
24460  * Ext JS Library 1.1.1
24461  * Copyright(c) 2006-2007, Ext JS, LLC.
24462  *
24463  * Originally Released Under LGPL - original licence link has changed is not relivant.
24464  *
24465  * Fork - LGPL
24466  * <script type="text/javascript">
24467  */
24468  
24469
24470 /**
24471  * @class Roo.ColorPalette
24472  * @extends Roo.Component
24473  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
24474  * Here's an example of typical usage:
24475  * <pre><code>
24476 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
24477 cp.render('my-div');
24478
24479 cp.on('select', function(palette, selColor){
24480     // do something with selColor
24481 });
24482 </code></pre>
24483  * @constructor
24484  * Create a new ColorPalette
24485  * @param {Object} config The config object
24486  */
24487 Roo.ColorPalette = function(config){
24488     Roo.ColorPalette.superclass.constructor.call(this, config);
24489     this.addEvents({
24490         /**
24491              * @event select
24492              * Fires when a color is selected
24493              * @param {ColorPalette} this
24494              * @param {String} color The 6-digit color hex code (without the # symbol)
24495              */
24496         select: true
24497     });
24498
24499     if(this.handler){
24500         this.on("select", this.handler, this.scope, true);
24501     }
24502 };
24503 Roo.extend(Roo.ColorPalette, Roo.Component, {
24504     /**
24505      * @cfg {String} itemCls
24506      * The CSS class to apply to the containing element (defaults to "x-color-palette")
24507      */
24508     itemCls : "x-color-palette",
24509     /**
24510      * @cfg {String} value
24511      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
24512      * the hex codes are case-sensitive.
24513      */
24514     value : null,
24515     clickEvent:'click',
24516     // private
24517     ctype: "Roo.ColorPalette",
24518
24519     /**
24520      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
24521      */
24522     allowReselect : false,
24523
24524     /**
24525      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
24526      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
24527      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
24528      * of colors with the width setting until the box is symmetrical.</p>
24529      * <p>You can override individual colors if needed:</p>
24530      * <pre><code>
24531 var cp = new Roo.ColorPalette();
24532 cp.colors[0] = "FF0000";  // change the first box to red
24533 </code></pre>
24534
24535 Or you can provide a custom array of your own for complete control:
24536 <pre><code>
24537 var cp = new Roo.ColorPalette();
24538 cp.colors = ["000000", "993300", "333300"];
24539 </code></pre>
24540      * @type Array
24541      */
24542     colors : [
24543         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
24544         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
24545         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
24546         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
24547         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
24548     ],
24549
24550     // private
24551     onRender : function(container, position){
24552         var t = new Roo.MasterTemplate(
24553             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
24554         );
24555         var c = this.colors;
24556         for(var i = 0, len = c.length; i < len; i++){
24557             t.add([c[i]]);
24558         }
24559         var el = document.createElement("div");
24560         el.className = this.itemCls;
24561         t.overwrite(el);
24562         container.dom.insertBefore(el, position);
24563         this.el = Roo.get(el);
24564         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
24565         if(this.clickEvent != 'click'){
24566             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
24567         }
24568     },
24569
24570     // private
24571     afterRender : function(){
24572         Roo.ColorPalette.superclass.afterRender.call(this);
24573         if(this.value){
24574             var s = this.value;
24575             this.value = null;
24576             this.select(s);
24577         }
24578     },
24579
24580     // private
24581     handleClick : function(e, t){
24582         e.preventDefault();
24583         if(!this.disabled){
24584             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
24585             this.select(c.toUpperCase());
24586         }
24587     },
24588
24589     /**
24590      * Selects the specified color in the palette (fires the select event)
24591      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
24592      */
24593     select : function(color){
24594         color = color.replace("#", "");
24595         if(color != this.value || this.allowReselect){
24596             var el = this.el;
24597             if(this.value){
24598                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
24599             }
24600             el.child("a.color-"+color).addClass("x-color-palette-sel");
24601             this.value = color;
24602             this.fireEvent("select", this, color);
24603         }
24604     }
24605 });/*
24606  * Based on:
24607  * Ext JS Library 1.1.1
24608  * Copyright(c) 2006-2007, Ext JS, LLC.
24609  *
24610  * Originally Released Under LGPL - original licence link has changed is not relivant.
24611  *
24612  * Fork - LGPL
24613  * <script type="text/javascript">
24614  */
24615  
24616 /**
24617  * @class Roo.DatePicker
24618  * @extends Roo.Component
24619  * Simple date picker class.
24620  * @constructor
24621  * Create a new DatePicker
24622  * @param {Object} config The config object
24623  */
24624 Roo.DatePicker = function(config){
24625     Roo.DatePicker.superclass.constructor.call(this, config);
24626
24627     this.value = config && config.value ?
24628                  config.value.clearTime() : new Date().clearTime();
24629
24630     this.addEvents({
24631         /**
24632              * @event select
24633              * Fires when a date is selected
24634              * @param {DatePicker} this
24635              * @param {Date} date The selected date
24636              */
24637         'select': true,
24638         /**
24639              * @event monthchange
24640              * Fires when the displayed month changes 
24641              * @param {DatePicker} this
24642              * @param {Date} date The selected month
24643              */
24644         'monthchange': true
24645     });
24646
24647     if(this.handler){
24648         this.on("select", this.handler,  this.scope || this);
24649     }
24650     // build the disabledDatesRE
24651     if(!this.disabledDatesRE && this.disabledDates){
24652         var dd = this.disabledDates;
24653         var re = "(?:";
24654         for(var i = 0; i < dd.length; i++){
24655             re += dd[i];
24656             if(i != dd.length-1) re += "|";
24657         }
24658         this.disabledDatesRE = new RegExp(re + ")");
24659     }
24660 };
24661
24662 Roo.extend(Roo.DatePicker, Roo.Component, {
24663     /**
24664      * @cfg {String} todayText
24665      * The text to display on the button that selects the current date (defaults to "Today")
24666      */
24667     todayText : "Today",
24668     /**
24669      * @cfg {String} okText
24670      * The text to display on the ok button
24671      */
24672     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
24673     /**
24674      * @cfg {String} cancelText
24675      * The text to display on the cancel button
24676      */
24677     cancelText : "Cancel",
24678     /**
24679      * @cfg {String} todayTip
24680      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
24681      */
24682     todayTip : "{0} (Spacebar)",
24683     /**
24684      * @cfg {Date} minDate
24685      * Minimum allowable date (JavaScript date object, defaults to null)
24686      */
24687     minDate : null,
24688     /**
24689      * @cfg {Date} maxDate
24690      * Maximum allowable date (JavaScript date object, defaults to null)
24691      */
24692     maxDate : null,
24693     /**
24694      * @cfg {String} minText
24695      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
24696      */
24697     minText : "This date is before the minimum date",
24698     /**
24699      * @cfg {String} maxText
24700      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
24701      */
24702     maxText : "This date is after the maximum date",
24703     /**
24704      * @cfg {String} format
24705      * The default date format string which can be overriden for localization support.  The format must be
24706      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
24707      */
24708     format : "m/d/y",
24709     /**
24710      * @cfg {Array} disabledDays
24711      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
24712      */
24713     disabledDays : null,
24714     /**
24715      * @cfg {String} disabledDaysText
24716      * The tooltip to display when the date falls on a disabled day (defaults to "")
24717      */
24718     disabledDaysText : "",
24719     /**
24720      * @cfg {RegExp} disabledDatesRE
24721      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
24722      */
24723     disabledDatesRE : null,
24724     /**
24725      * @cfg {String} disabledDatesText
24726      * The tooltip text to display when the date falls on a disabled date (defaults to "")
24727      */
24728     disabledDatesText : "",
24729     /**
24730      * @cfg {Boolean} constrainToViewport
24731      * True to constrain the date picker to the viewport (defaults to true)
24732      */
24733     constrainToViewport : true,
24734     /**
24735      * @cfg {Array} monthNames
24736      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
24737      */
24738     monthNames : Date.monthNames,
24739     /**
24740      * @cfg {Array} dayNames
24741      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
24742      */
24743     dayNames : Date.dayNames,
24744     /**
24745      * @cfg {String} nextText
24746      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
24747      */
24748     nextText: 'Next Month (Control+Right)',
24749     /**
24750      * @cfg {String} prevText
24751      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
24752      */
24753     prevText: 'Previous Month (Control+Left)',
24754     /**
24755      * @cfg {String} monthYearText
24756      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
24757      */
24758     monthYearText: 'Choose a month (Control+Up/Down to move years)',
24759     /**
24760      * @cfg {Number} startDay
24761      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
24762      */
24763     startDay : 0,
24764     /**
24765      * @cfg {Bool} showClear
24766      * Show a clear button (usefull for date form elements that can be blank.)
24767      */
24768     
24769     showClear: false,
24770     
24771     /**
24772      * Sets the value of the date field
24773      * @param {Date} value The date to set
24774      */
24775     setValue : function(value){
24776         var old = this.value;
24777         this.value = value.clearTime(true);
24778         if(this.el){
24779             this.update(this.value);
24780         }
24781     },
24782
24783     /**
24784      * Gets the current selected value of the date field
24785      * @return {Date} The selected date
24786      */
24787     getValue : function(){
24788         return this.value;
24789     },
24790
24791     // private
24792     focus : function(){
24793         if(this.el){
24794             this.update(this.activeDate);
24795         }
24796     },
24797
24798     // private
24799     onRender : function(container, position){
24800         var m = [
24801              '<table cellspacing="0">',
24802                 '<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>',
24803                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
24804         var dn = this.dayNames;
24805         for(var i = 0; i < 7; i++){
24806             var d = this.startDay+i;
24807             if(d > 6){
24808                 d = d-7;
24809             }
24810             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
24811         }
24812         m[m.length] = "</tr></thead><tbody><tr>";
24813         for(var i = 0; i < 42; i++) {
24814             if(i % 7 == 0 && i != 0){
24815                 m[m.length] = "</tr><tr>";
24816             }
24817             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
24818         }
24819         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
24820             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
24821
24822         var el = document.createElement("div");
24823         el.className = "x-date-picker";
24824         el.innerHTML = m.join("");
24825
24826         container.dom.insertBefore(el, position);
24827
24828         this.el = Roo.get(el);
24829         this.eventEl = Roo.get(el.firstChild);
24830
24831         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
24832             handler: this.showPrevMonth,
24833             scope: this,
24834             preventDefault:true,
24835             stopDefault:true
24836         });
24837
24838         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
24839             handler: this.showNextMonth,
24840             scope: this,
24841             preventDefault:true,
24842             stopDefault:true
24843         });
24844
24845         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
24846
24847         this.monthPicker = this.el.down('div.x-date-mp');
24848         this.monthPicker.enableDisplayMode('block');
24849         
24850         var kn = new Roo.KeyNav(this.eventEl, {
24851             "left" : function(e){
24852                 e.ctrlKey ?
24853                     this.showPrevMonth() :
24854                     this.update(this.activeDate.add("d", -1));
24855             },
24856
24857             "right" : function(e){
24858                 e.ctrlKey ?
24859                     this.showNextMonth() :
24860                     this.update(this.activeDate.add("d", 1));
24861             },
24862
24863             "up" : function(e){
24864                 e.ctrlKey ?
24865                     this.showNextYear() :
24866                     this.update(this.activeDate.add("d", -7));
24867             },
24868
24869             "down" : function(e){
24870                 e.ctrlKey ?
24871                     this.showPrevYear() :
24872                     this.update(this.activeDate.add("d", 7));
24873             },
24874
24875             "pageUp" : function(e){
24876                 this.showNextMonth();
24877             },
24878
24879             "pageDown" : function(e){
24880                 this.showPrevMonth();
24881             },
24882
24883             "enter" : function(e){
24884                 e.stopPropagation();
24885                 return true;
24886             },
24887
24888             scope : this
24889         });
24890
24891         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
24892
24893         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
24894
24895         this.el.unselectable();
24896         
24897         this.cells = this.el.select("table.x-date-inner tbody td");
24898         this.textNodes = this.el.query("table.x-date-inner tbody span");
24899
24900         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
24901             text: "&#160;",
24902             tooltip: this.monthYearText
24903         });
24904
24905         this.mbtn.on('click', this.showMonthPicker, this);
24906         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
24907
24908
24909         var today = (new Date()).dateFormat(this.format);
24910         
24911         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
24912         if (this.showClear) {
24913             baseTb.add( new Roo.Toolbar.Fill());
24914         }
24915         baseTb.add({
24916             text: String.format(this.todayText, today),
24917             tooltip: String.format(this.todayTip, today),
24918             handler: this.selectToday,
24919             scope: this
24920         });
24921         
24922         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
24923             
24924         //});
24925         if (this.showClear) {
24926             
24927             baseTb.add( new Roo.Toolbar.Fill());
24928             baseTb.add({
24929                 text: '&#160;',
24930                 cls: 'x-btn-icon x-btn-clear',
24931                 handler: function() {
24932                     //this.value = '';
24933                     this.fireEvent("select", this, '');
24934                 },
24935                 scope: this
24936             });
24937         }
24938         
24939         
24940         if(Roo.isIE){
24941             this.el.repaint();
24942         }
24943         this.update(this.value);
24944     },
24945
24946     createMonthPicker : function(){
24947         if(!this.monthPicker.dom.firstChild){
24948             var buf = ['<table border="0" cellspacing="0">'];
24949             for(var i = 0; i < 6; i++){
24950                 buf.push(
24951                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
24952                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
24953                     i == 0 ?
24954                     '<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>' :
24955                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
24956                 );
24957             }
24958             buf.push(
24959                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
24960                     this.okText,
24961                     '</button><button type="button" class="x-date-mp-cancel">',
24962                     this.cancelText,
24963                     '</button></td></tr>',
24964                 '</table>'
24965             );
24966             this.monthPicker.update(buf.join(''));
24967             this.monthPicker.on('click', this.onMonthClick, this);
24968             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
24969
24970             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
24971             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
24972
24973             this.mpMonths.each(function(m, a, i){
24974                 i += 1;
24975                 if((i%2) == 0){
24976                     m.dom.xmonth = 5 + Math.round(i * .5);
24977                 }else{
24978                     m.dom.xmonth = Math.round((i-1) * .5);
24979                 }
24980             });
24981         }
24982     },
24983
24984     showMonthPicker : function(){
24985         this.createMonthPicker();
24986         var size = this.el.getSize();
24987         this.monthPicker.setSize(size);
24988         this.monthPicker.child('table').setSize(size);
24989
24990         this.mpSelMonth = (this.activeDate || this.value).getMonth();
24991         this.updateMPMonth(this.mpSelMonth);
24992         this.mpSelYear = (this.activeDate || this.value).getFullYear();
24993         this.updateMPYear(this.mpSelYear);
24994
24995         this.monthPicker.slideIn('t', {duration:.2});
24996     },
24997
24998     updateMPYear : function(y){
24999         this.mpyear = y;
25000         var ys = this.mpYears.elements;
25001         for(var i = 1; i <= 10; i++){
25002             var td = ys[i-1], y2;
25003             if((i%2) == 0){
25004                 y2 = y + Math.round(i * .5);
25005                 td.firstChild.innerHTML = y2;
25006                 td.xyear = y2;
25007             }else{
25008                 y2 = y - (5-Math.round(i * .5));
25009                 td.firstChild.innerHTML = y2;
25010                 td.xyear = y2;
25011             }
25012             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
25013         }
25014     },
25015
25016     updateMPMonth : function(sm){
25017         this.mpMonths.each(function(m, a, i){
25018             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
25019         });
25020     },
25021
25022     selectMPMonth: function(m){
25023         
25024     },
25025
25026     onMonthClick : function(e, t){
25027         e.stopEvent();
25028         var el = new Roo.Element(t), pn;
25029         if(el.is('button.x-date-mp-cancel')){
25030             this.hideMonthPicker();
25031         }
25032         else if(el.is('button.x-date-mp-ok')){
25033             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
25034             this.hideMonthPicker();
25035         }
25036         else if(pn = el.up('td.x-date-mp-month', 2)){
25037             this.mpMonths.removeClass('x-date-mp-sel');
25038             pn.addClass('x-date-mp-sel');
25039             this.mpSelMonth = pn.dom.xmonth;
25040         }
25041         else if(pn = el.up('td.x-date-mp-year', 2)){
25042             this.mpYears.removeClass('x-date-mp-sel');
25043             pn.addClass('x-date-mp-sel');
25044             this.mpSelYear = pn.dom.xyear;
25045         }
25046         else if(el.is('a.x-date-mp-prev')){
25047             this.updateMPYear(this.mpyear-10);
25048         }
25049         else if(el.is('a.x-date-mp-next')){
25050             this.updateMPYear(this.mpyear+10);
25051         }
25052     },
25053
25054     onMonthDblClick : function(e, t){
25055         e.stopEvent();
25056         var el = new Roo.Element(t), pn;
25057         if(pn = el.up('td.x-date-mp-month', 2)){
25058             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
25059             this.hideMonthPicker();
25060         }
25061         else if(pn = el.up('td.x-date-mp-year', 2)){
25062             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
25063             this.hideMonthPicker();
25064         }
25065     },
25066
25067     hideMonthPicker : function(disableAnim){
25068         if(this.monthPicker){
25069             if(disableAnim === true){
25070                 this.monthPicker.hide();
25071             }else{
25072                 this.monthPicker.slideOut('t', {duration:.2});
25073             }
25074         }
25075     },
25076
25077     // private
25078     showPrevMonth : function(e){
25079         this.update(this.activeDate.add("mo", -1));
25080     },
25081
25082     // private
25083     showNextMonth : function(e){
25084         this.update(this.activeDate.add("mo", 1));
25085     },
25086
25087     // private
25088     showPrevYear : function(){
25089         this.update(this.activeDate.add("y", -1));
25090     },
25091
25092     // private
25093     showNextYear : function(){
25094         this.update(this.activeDate.add("y", 1));
25095     },
25096
25097     // private
25098     handleMouseWheel : function(e){
25099         var delta = e.getWheelDelta();
25100         if(delta > 0){
25101             this.showPrevMonth();
25102             e.stopEvent();
25103         } else if(delta < 0){
25104             this.showNextMonth();
25105             e.stopEvent();
25106         }
25107     },
25108
25109     // private
25110     handleDateClick : function(e, t){
25111         e.stopEvent();
25112         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
25113             this.setValue(new Date(t.dateValue));
25114             this.fireEvent("select", this, this.value);
25115         }
25116     },
25117
25118     // private
25119     selectToday : function(){
25120         this.setValue(new Date().clearTime());
25121         this.fireEvent("select", this, this.value);
25122     },
25123
25124     // private
25125     update : function(date)
25126     {
25127         var vd = this.activeDate;
25128         this.activeDate = date;
25129         if(vd && this.el){
25130             var t = date.getTime();
25131             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
25132                 this.cells.removeClass("x-date-selected");
25133                 this.cells.each(function(c){
25134                    if(c.dom.firstChild.dateValue == t){
25135                        c.addClass("x-date-selected");
25136                        setTimeout(function(){
25137                             try{c.dom.firstChild.focus();}catch(e){}
25138                        }, 50);
25139                        return false;
25140                    }
25141                 });
25142                 return;
25143             }
25144         }
25145         
25146         var days = date.getDaysInMonth();
25147         var firstOfMonth = date.getFirstDateOfMonth();
25148         var startingPos = firstOfMonth.getDay()-this.startDay;
25149
25150         if(startingPos <= this.startDay){
25151             startingPos += 7;
25152         }
25153
25154         var pm = date.add("mo", -1);
25155         var prevStart = pm.getDaysInMonth()-startingPos;
25156
25157         var cells = this.cells.elements;
25158         var textEls = this.textNodes;
25159         days += startingPos;
25160
25161         // convert everything to numbers so it's fast
25162         var day = 86400000;
25163         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
25164         var today = new Date().clearTime().getTime();
25165         var sel = date.clearTime().getTime();
25166         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
25167         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
25168         var ddMatch = this.disabledDatesRE;
25169         var ddText = this.disabledDatesText;
25170         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
25171         var ddaysText = this.disabledDaysText;
25172         var format = this.format;
25173
25174         var setCellClass = function(cal, cell){
25175             cell.title = "";
25176             var t = d.getTime();
25177             cell.firstChild.dateValue = t;
25178             if(t == today){
25179                 cell.className += " x-date-today";
25180                 cell.title = cal.todayText;
25181             }
25182             if(t == sel){
25183                 cell.className += " x-date-selected";
25184                 setTimeout(function(){
25185                     try{cell.firstChild.focus();}catch(e){}
25186                 }, 50);
25187             }
25188             // disabling
25189             if(t < min) {
25190                 cell.className = " x-date-disabled";
25191                 cell.title = cal.minText;
25192                 return;
25193             }
25194             if(t > max) {
25195                 cell.className = " x-date-disabled";
25196                 cell.title = cal.maxText;
25197                 return;
25198             }
25199             if(ddays){
25200                 if(ddays.indexOf(d.getDay()) != -1){
25201                     cell.title = ddaysText;
25202                     cell.className = " x-date-disabled";
25203                 }
25204             }
25205             if(ddMatch && format){
25206                 var fvalue = d.dateFormat(format);
25207                 if(ddMatch.test(fvalue)){
25208                     cell.title = ddText.replace("%0", fvalue);
25209                     cell.className = " x-date-disabled";
25210                 }
25211             }
25212         };
25213
25214         var i = 0;
25215         for(; i < startingPos; i++) {
25216             textEls[i].innerHTML = (++prevStart);
25217             d.setDate(d.getDate()+1);
25218             cells[i].className = "x-date-prevday";
25219             setCellClass(this, cells[i]);
25220         }
25221         for(; i < days; i++){
25222             intDay = i - startingPos + 1;
25223             textEls[i].innerHTML = (intDay);
25224             d.setDate(d.getDate()+1);
25225             cells[i].className = "x-date-active";
25226             setCellClass(this, cells[i]);
25227         }
25228         var extraDays = 0;
25229         for(; i < 42; i++) {
25230              textEls[i].innerHTML = (++extraDays);
25231              d.setDate(d.getDate()+1);
25232              cells[i].className = "x-date-nextday";
25233              setCellClass(this, cells[i]);
25234         }
25235
25236         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
25237         this.fireEvent('monthchange', this, date);
25238         
25239         if(!this.internalRender){
25240             var main = this.el.dom.firstChild;
25241             var w = main.offsetWidth;
25242             this.el.setWidth(w + this.el.getBorderWidth("lr"));
25243             Roo.fly(main).setWidth(w);
25244             this.internalRender = true;
25245             // opera does not respect the auto grow header center column
25246             // then, after it gets a width opera refuses to recalculate
25247             // without a second pass
25248             if(Roo.isOpera && !this.secondPass){
25249                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
25250                 this.secondPass = true;
25251                 this.update.defer(10, this, [date]);
25252             }
25253         }
25254         
25255         
25256     }
25257 });        /*
25258  * Based on:
25259  * Ext JS Library 1.1.1
25260  * Copyright(c) 2006-2007, Ext JS, LLC.
25261  *
25262  * Originally Released Under LGPL - original licence link has changed is not relivant.
25263  *
25264  * Fork - LGPL
25265  * <script type="text/javascript">
25266  */
25267 /**
25268  * @class Roo.TabPanel
25269  * @extends Roo.util.Observable
25270  * A lightweight tab container.
25271  * <br><br>
25272  * Usage:
25273  * <pre><code>
25274 // basic tabs 1, built from existing content
25275 var tabs = new Roo.TabPanel("tabs1");
25276 tabs.addTab("script", "View Script");
25277 tabs.addTab("markup", "View Markup");
25278 tabs.activate("script");
25279
25280 // more advanced tabs, built from javascript
25281 var jtabs = new Roo.TabPanel("jtabs");
25282 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
25283
25284 // set up the UpdateManager
25285 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
25286 var updater = tab2.getUpdateManager();
25287 updater.setDefaultUrl("ajax1.htm");
25288 tab2.on('activate', updater.refresh, updater, true);
25289
25290 // Use setUrl for Ajax loading
25291 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
25292 tab3.setUrl("ajax2.htm", null, true);
25293
25294 // Disabled tab
25295 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
25296 tab4.disable();
25297
25298 jtabs.activate("jtabs-1");
25299  * </code></pre>
25300  * @constructor
25301  * Create a new TabPanel.
25302  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
25303  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
25304  */
25305 Roo.TabPanel = function(container, config){
25306     /**
25307     * The container element for this TabPanel.
25308     * @type Roo.Element
25309     */
25310     this.el = Roo.get(container, true);
25311     if(config){
25312         if(typeof config == "boolean"){
25313             this.tabPosition = config ? "bottom" : "top";
25314         }else{
25315             Roo.apply(this, config);
25316         }
25317     }
25318     if(this.tabPosition == "bottom"){
25319         this.bodyEl = Roo.get(this.createBody(this.el.dom));
25320         this.el.addClass("x-tabs-bottom");
25321     }
25322     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
25323     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
25324     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
25325     if(Roo.isIE){
25326         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
25327     }
25328     if(this.tabPosition != "bottom"){
25329         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
25330          * @type Roo.Element
25331          */
25332         this.bodyEl = Roo.get(this.createBody(this.el.dom));
25333         this.el.addClass("x-tabs-top");
25334     }
25335     this.items = [];
25336
25337     this.bodyEl.setStyle("position", "relative");
25338
25339     this.active = null;
25340     this.activateDelegate = this.activate.createDelegate(this);
25341
25342     this.addEvents({
25343         /**
25344          * @event tabchange
25345          * Fires when the active tab changes
25346          * @param {Roo.TabPanel} this
25347          * @param {Roo.TabPanelItem} activePanel The new active tab
25348          */
25349         "tabchange": true,
25350         /**
25351          * @event beforetabchange
25352          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
25353          * @param {Roo.TabPanel} this
25354          * @param {Object} e Set cancel to true on this object to cancel the tab change
25355          * @param {Roo.TabPanelItem} tab The tab being changed to
25356          */
25357         "beforetabchange" : true
25358     });
25359
25360     Roo.EventManager.onWindowResize(this.onResize, this);
25361     this.cpad = this.el.getPadding("lr");
25362     this.hiddenCount = 0;
25363
25364
25365     // toolbar on the tabbar support...
25366     if (this.toolbar) {
25367         var tcfg = this.toolbar;
25368         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
25369         this.toolbar = new Roo.Toolbar(tcfg);
25370         if (Roo.isSafari) {
25371             var tbl = tcfg.container.child('table', true);
25372             tbl.setAttribute('width', '100%');
25373         }
25374         
25375     }
25376    
25377
25378
25379     Roo.TabPanel.superclass.constructor.call(this);
25380 };
25381
25382 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
25383     /*
25384      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
25385      */
25386     tabPosition : "top",
25387     /*
25388      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
25389      */
25390     currentTabWidth : 0,
25391     /*
25392      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
25393      */
25394     minTabWidth : 40,
25395     /*
25396      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
25397      */
25398     maxTabWidth : 250,
25399     /*
25400      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
25401      */
25402     preferredTabWidth : 175,
25403     /*
25404      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
25405      */
25406     resizeTabs : false,
25407     /*
25408      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
25409      */
25410     monitorResize : true,
25411     /*
25412      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
25413      */
25414     toolbar : false,
25415
25416     /**
25417      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
25418      * @param {String} id The id of the div to use <b>or create</b>
25419      * @param {String} text The text for the tab
25420      * @param {String} content (optional) Content to put in the TabPanelItem body
25421      * @param {Boolean} closable (optional) True to create a close icon on the tab
25422      * @return {Roo.TabPanelItem} The created TabPanelItem
25423      */
25424     addTab : function(id, text, content, closable){
25425         var item = new Roo.TabPanelItem(this, id, text, closable);
25426         this.addTabItem(item);
25427         if(content){
25428             item.setContent(content);
25429         }
25430         return item;
25431     },
25432
25433     /**
25434      * Returns the {@link Roo.TabPanelItem} with the specified id/index
25435      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
25436      * @return {Roo.TabPanelItem}
25437      */
25438     getTab : function(id){
25439         return this.items[id];
25440     },
25441
25442     /**
25443      * Hides the {@link Roo.TabPanelItem} with the specified id/index
25444      * @param {String/Number} id The id or index of the TabPanelItem to hide.
25445      */
25446     hideTab : function(id){
25447         var t = this.items[id];
25448         if(!t.isHidden()){
25449            t.setHidden(true);
25450            this.hiddenCount++;
25451            this.autoSizeTabs();
25452         }
25453     },
25454
25455     /**
25456      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
25457      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
25458      */
25459     unhideTab : function(id){
25460         var t = this.items[id];
25461         if(t.isHidden()){
25462            t.setHidden(false);
25463            this.hiddenCount--;
25464            this.autoSizeTabs();
25465         }
25466     },
25467
25468     /**
25469      * Adds an existing {@link Roo.TabPanelItem}.
25470      * @param {Roo.TabPanelItem} item The TabPanelItem to add
25471      */
25472     addTabItem : function(item){
25473         this.items[item.id] = item;
25474         this.items.push(item);
25475         if(this.resizeTabs){
25476            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
25477            this.autoSizeTabs();
25478         }else{
25479             item.autoSize();
25480         }
25481     },
25482
25483     /**
25484      * Removes a {@link Roo.TabPanelItem}.
25485      * @param {String/Number} id The id or index of the TabPanelItem to remove.
25486      */
25487     removeTab : function(id){
25488         var items = this.items;
25489         var tab = items[id];
25490         if(!tab) { return; }
25491         var index = items.indexOf(tab);
25492         if(this.active == tab && items.length > 1){
25493             var newTab = this.getNextAvailable(index);
25494             if(newTab) {
25495                 newTab.activate();
25496             }
25497         }
25498         this.stripEl.dom.removeChild(tab.pnode.dom);
25499         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
25500             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
25501         }
25502         items.splice(index, 1);
25503         delete this.items[tab.id];
25504         tab.fireEvent("close", tab);
25505         tab.purgeListeners();
25506         this.autoSizeTabs();
25507     },
25508
25509     getNextAvailable : function(start){
25510         var items = this.items;
25511         var index = start;
25512         // look for a next tab that will slide over to
25513         // replace the one being removed
25514         while(index < items.length){
25515             var item = items[++index];
25516             if(item && !item.isHidden()){
25517                 return item;
25518             }
25519         }
25520         // if one isn't found select the previous tab (on the left)
25521         index = start;
25522         while(index >= 0){
25523             var item = items[--index];
25524             if(item && !item.isHidden()){
25525                 return item;
25526             }
25527         }
25528         return null;
25529     },
25530
25531     /**
25532      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
25533      * @param {String/Number} id The id or index of the TabPanelItem to disable.
25534      */
25535     disableTab : function(id){
25536         var tab = this.items[id];
25537         if(tab && this.active != tab){
25538             tab.disable();
25539         }
25540     },
25541
25542     /**
25543      * Enables a {@link Roo.TabPanelItem} that is disabled.
25544      * @param {String/Number} id The id or index of the TabPanelItem to enable.
25545      */
25546     enableTab : function(id){
25547         var tab = this.items[id];
25548         tab.enable();
25549     },
25550
25551     /**
25552      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
25553      * @param {String/Number} id The id or index of the TabPanelItem to activate.
25554      * @return {Roo.TabPanelItem} The TabPanelItem.
25555      */
25556     activate : function(id){
25557         var tab = this.items[id];
25558         if(!tab){
25559             return null;
25560         }
25561         if(tab == this.active || tab.disabled){
25562             return tab;
25563         }
25564         var e = {};
25565         this.fireEvent("beforetabchange", this, e, tab);
25566         if(e.cancel !== true && !tab.disabled){
25567             if(this.active){
25568                 this.active.hide();
25569             }
25570             this.active = this.items[id];
25571             this.active.show();
25572             this.fireEvent("tabchange", this, this.active);
25573         }
25574         return tab;
25575     },
25576
25577     /**
25578      * Gets the active {@link Roo.TabPanelItem}.
25579      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
25580      */
25581     getActiveTab : function(){
25582         return this.active;
25583     },
25584
25585     /**
25586      * Updates the tab body element to fit the height of the container element
25587      * for overflow scrolling
25588      * @param {Number} targetHeight (optional) Override the starting height from the elements height
25589      */
25590     syncHeight : function(targetHeight){
25591         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
25592         var bm = this.bodyEl.getMargins();
25593         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
25594         this.bodyEl.setHeight(newHeight);
25595         return newHeight;
25596     },
25597
25598     onResize : function(){
25599         if(this.monitorResize){
25600             this.autoSizeTabs();
25601         }
25602     },
25603
25604     /**
25605      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
25606      */
25607     beginUpdate : function(){
25608         this.updating = true;
25609     },
25610
25611     /**
25612      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
25613      */
25614     endUpdate : function(){
25615         this.updating = false;
25616         this.autoSizeTabs();
25617     },
25618
25619     /**
25620      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
25621      */
25622     autoSizeTabs : function(){
25623         var count = this.items.length;
25624         var vcount = count - this.hiddenCount;
25625         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
25626         var w = Math.max(this.el.getWidth() - this.cpad, 10);
25627         var availWidth = Math.floor(w / vcount);
25628         var b = this.stripBody;
25629         if(b.getWidth() > w){
25630             var tabs = this.items;
25631             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
25632             if(availWidth < this.minTabWidth){
25633                 /*if(!this.sleft){    // incomplete scrolling code
25634                     this.createScrollButtons();
25635                 }
25636                 this.showScroll();
25637                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
25638             }
25639         }else{
25640             if(this.currentTabWidth < this.preferredTabWidth){
25641                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
25642             }
25643         }
25644     },
25645
25646     /**
25647      * Returns the number of tabs in this TabPanel.
25648      * @return {Number}
25649      */
25650      getCount : function(){
25651          return this.items.length;
25652      },
25653
25654     /**
25655      * Resizes all the tabs to the passed width
25656      * @param {Number} The new width
25657      */
25658     setTabWidth : function(width){
25659         this.currentTabWidth = width;
25660         for(var i = 0, len = this.items.length; i < len; i++) {
25661                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
25662         }
25663     },
25664
25665     /**
25666      * Destroys this TabPanel
25667      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
25668      */
25669     destroy : function(removeEl){
25670         Roo.EventManager.removeResizeListener(this.onResize, this);
25671         for(var i = 0, len = this.items.length; i < len; i++){
25672             this.items[i].purgeListeners();
25673         }
25674         if(removeEl === true){
25675             this.el.update("");
25676             this.el.remove();
25677         }
25678     }
25679 });
25680
25681 /**
25682  * @class Roo.TabPanelItem
25683  * @extends Roo.util.Observable
25684  * Represents an individual item (tab plus body) in a TabPanel.
25685  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
25686  * @param {String} id The id of this TabPanelItem
25687  * @param {String} text The text for the tab of this TabPanelItem
25688  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
25689  */
25690 Roo.TabPanelItem = function(tabPanel, id, text, closable){
25691     /**
25692      * The {@link Roo.TabPanel} this TabPanelItem belongs to
25693      * @type Roo.TabPanel
25694      */
25695     this.tabPanel = tabPanel;
25696     /**
25697      * The id for this TabPanelItem
25698      * @type String
25699      */
25700     this.id = id;
25701     /** @private */
25702     this.disabled = false;
25703     /** @private */
25704     this.text = text;
25705     /** @private */
25706     this.loaded = false;
25707     this.closable = closable;
25708
25709     /**
25710      * The body element for this TabPanelItem.
25711      * @type Roo.Element
25712      */
25713     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
25714     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
25715     this.bodyEl.setStyle("display", "block");
25716     this.bodyEl.setStyle("zoom", "1");
25717     this.hideAction();
25718
25719     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
25720     /** @private */
25721     this.el = Roo.get(els.el, true);
25722     this.inner = Roo.get(els.inner, true);
25723     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
25724     this.pnode = Roo.get(els.el.parentNode, true);
25725     this.el.on("mousedown", this.onTabMouseDown, this);
25726     this.el.on("click", this.onTabClick, this);
25727     /** @private */
25728     if(closable){
25729         var c = Roo.get(els.close, true);
25730         c.dom.title = this.closeText;
25731         c.addClassOnOver("close-over");
25732         c.on("click", this.closeClick, this);
25733      }
25734
25735     this.addEvents({
25736          /**
25737          * @event activate
25738          * Fires when this tab becomes the active tab.
25739          * @param {Roo.TabPanel} tabPanel The parent TabPanel
25740          * @param {Roo.TabPanelItem} this
25741          */
25742         "activate": true,
25743         /**
25744          * @event beforeclose
25745          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
25746          * @param {Roo.TabPanelItem} this
25747          * @param {Object} e Set cancel to true on this object to cancel the close.
25748          */
25749         "beforeclose": true,
25750         /**
25751          * @event close
25752          * Fires when this tab is closed.
25753          * @param {Roo.TabPanelItem} this
25754          */
25755          "close": true,
25756         /**
25757          * @event deactivate
25758          * Fires when this tab is no longer the active tab.
25759          * @param {Roo.TabPanel} tabPanel The parent TabPanel
25760          * @param {Roo.TabPanelItem} this
25761          */
25762          "deactivate" : true
25763     });
25764     this.hidden = false;
25765
25766     Roo.TabPanelItem.superclass.constructor.call(this);
25767 };
25768
25769 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
25770     purgeListeners : function(){
25771        Roo.util.Observable.prototype.purgeListeners.call(this);
25772        this.el.removeAllListeners();
25773     },
25774     /**
25775      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
25776      */
25777     show : function(){
25778         this.pnode.addClass("on");
25779         this.showAction();
25780         if(Roo.isOpera){
25781             this.tabPanel.stripWrap.repaint();
25782         }
25783         this.fireEvent("activate", this.tabPanel, this);
25784     },
25785
25786     /**
25787      * Returns true if this tab is the active tab.
25788      * @return {Boolean}
25789      */
25790     isActive : function(){
25791         return this.tabPanel.getActiveTab() == this;
25792     },
25793
25794     /**
25795      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
25796      */
25797     hide : function(){
25798         this.pnode.removeClass("on");
25799         this.hideAction();
25800         this.fireEvent("deactivate", this.tabPanel, this);
25801     },
25802
25803     hideAction : function(){
25804         this.bodyEl.hide();
25805         this.bodyEl.setStyle("position", "absolute");
25806         this.bodyEl.setLeft("-20000px");
25807         this.bodyEl.setTop("-20000px");
25808     },
25809
25810     showAction : function(){
25811         this.bodyEl.setStyle("position", "relative");
25812         this.bodyEl.setTop("");
25813         this.bodyEl.setLeft("");
25814         this.bodyEl.show();
25815     },
25816
25817     /**
25818      * Set the tooltip for the tab.
25819      * @param {String} tooltip The tab's tooltip
25820      */
25821     setTooltip : function(text){
25822         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
25823             this.textEl.dom.qtip = text;
25824             this.textEl.dom.removeAttribute('title');
25825         }else{
25826             this.textEl.dom.title = text;
25827         }
25828     },
25829
25830     onTabClick : function(e){
25831         e.preventDefault();
25832         this.tabPanel.activate(this.id);
25833     },
25834
25835     onTabMouseDown : function(e){
25836         e.preventDefault();
25837         this.tabPanel.activate(this.id);
25838     },
25839
25840     getWidth : function(){
25841         return this.inner.getWidth();
25842     },
25843
25844     setWidth : function(width){
25845         var iwidth = width - this.pnode.getPadding("lr");
25846         this.inner.setWidth(iwidth);
25847         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
25848         this.pnode.setWidth(width);
25849     },
25850
25851     /**
25852      * Show or hide the tab
25853      * @param {Boolean} hidden True to hide or false to show.
25854      */
25855     setHidden : function(hidden){
25856         this.hidden = hidden;
25857         this.pnode.setStyle("display", hidden ? "none" : "");
25858     },
25859
25860     /**
25861      * Returns true if this tab is "hidden"
25862      * @return {Boolean}
25863      */
25864     isHidden : function(){
25865         return this.hidden;
25866     },
25867
25868     /**
25869      * Returns the text for this tab
25870      * @return {String}
25871      */
25872     getText : function(){
25873         return this.text;
25874     },
25875
25876     autoSize : function(){
25877         //this.el.beginMeasure();
25878         this.textEl.setWidth(1);
25879         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
25880         //this.el.endMeasure();
25881     },
25882
25883     /**
25884      * Sets the text for the tab (Note: this also sets the tooltip text)
25885      * @param {String} text The tab's text and tooltip
25886      */
25887     setText : function(text){
25888         this.text = text;
25889         this.textEl.update(text);
25890         this.setTooltip(text);
25891         if(!this.tabPanel.resizeTabs){
25892             this.autoSize();
25893         }
25894     },
25895     /**
25896      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
25897      */
25898     activate : function(){
25899         this.tabPanel.activate(this.id);
25900     },
25901
25902     /**
25903      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
25904      */
25905     disable : function(){
25906         if(this.tabPanel.active != this){
25907             this.disabled = true;
25908             this.pnode.addClass("disabled");
25909         }
25910     },
25911
25912     /**
25913      * Enables this TabPanelItem if it was previously disabled.
25914      */
25915     enable : function(){
25916         this.disabled = false;
25917         this.pnode.removeClass("disabled");
25918     },
25919
25920     /**
25921      * Sets the content for this TabPanelItem.
25922      * @param {String} content The content
25923      * @param {Boolean} loadScripts true to look for and load scripts
25924      */
25925     setContent : function(content, loadScripts){
25926         this.bodyEl.update(content, loadScripts);
25927     },
25928
25929     /**
25930      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
25931      * @return {Roo.UpdateManager} The UpdateManager
25932      */
25933     getUpdateManager : function(){
25934         return this.bodyEl.getUpdateManager();
25935     },
25936
25937     /**
25938      * Set a URL to be used to load the content for this TabPanelItem.
25939      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
25940      * @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)
25941      * @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)
25942      * @return {Roo.UpdateManager} The UpdateManager
25943      */
25944     setUrl : function(url, params, loadOnce){
25945         if(this.refreshDelegate){
25946             this.un('activate', this.refreshDelegate);
25947         }
25948         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
25949         this.on("activate", this.refreshDelegate);
25950         return this.bodyEl.getUpdateManager();
25951     },
25952
25953     /** @private */
25954     _handleRefresh : function(url, params, loadOnce){
25955         if(!loadOnce || !this.loaded){
25956             var updater = this.bodyEl.getUpdateManager();
25957             updater.update(url, params, this._setLoaded.createDelegate(this));
25958         }
25959     },
25960
25961     /**
25962      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
25963      *   Will fail silently if the setUrl method has not been called.
25964      *   This does not activate the panel, just updates its content.
25965      */
25966     refresh : function(){
25967         if(this.refreshDelegate){
25968            this.loaded = false;
25969            this.refreshDelegate();
25970         }
25971     },
25972
25973     /** @private */
25974     _setLoaded : function(){
25975         this.loaded = true;
25976     },
25977
25978     /** @private */
25979     closeClick : function(e){
25980         var o = {};
25981         e.stopEvent();
25982         this.fireEvent("beforeclose", this, o);
25983         if(o.cancel !== true){
25984             this.tabPanel.removeTab(this.id);
25985         }
25986     },
25987     /**
25988      * The text displayed in the tooltip for the close icon.
25989      * @type String
25990      */
25991     closeText : "Close this tab"
25992 });
25993
25994 /** @private */
25995 Roo.TabPanel.prototype.createStrip = function(container){
25996     var strip = document.createElement("div");
25997     strip.className = "x-tabs-wrap";
25998     container.appendChild(strip);
25999     return strip;
26000 };
26001 /** @private */
26002 Roo.TabPanel.prototype.createStripList = function(strip){
26003     // div wrapper for retard IE
26004     // returns the "tr" element.
26005     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
26006         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
26007         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
26008     return strip.firstChild.firstChild.firstChild.firstChild;
26009 };
26010 /** @private */
26011 Roo.TabPanel.prototype.createBody = function(container){
26012     var body = document.createElement("div");
26013     Roo.id(body, "tab-body");
26014     Roo.fly(body).addClass("x-tabs-body");
26015     container.appendChild(body);
26016     return body;
26017 };
26018 /** @private */
26019 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
26020     var body = Roo.getDom(id);
26021     if(!body){
26022         body = document.createElement("div");
26023         body.id = id;
26024     }
26025     Roo.fly(body).addClass("x-tabs-item-body");
26026     bodyEl.insertBefore(body, bodyEl.firstChild);
26027     return body;
26028 };
26029 /** @private */
26030 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
26031     var td = document.createElement("td");
26032     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
26033     //stripEl.appendChild(td);
26034     if(closable){
26035         td.className = "x-tabs-closable";
26036         if(!this.closeTpl){
26037             this.closeTpl = new Roo.Template(
26038                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
26039                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
26040                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
26041             );
26042         }
26043         var el = this.closeTpl.overwrite(td, {"text": text});
26044         var close = el.getElementsByTagName("div")[0];
26045         var inner = el.getElementsByTagName("em")[0];
26046         return {"el": el, "close": close, "inner": inner};
26047     } else {
26048         if(!this.tabTpl){
26049             this.tabTpl = new Roo.Template(
26050                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
26051                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
26052             );
26053         }
26054         var el = this.tabTpl.overwrite(td, {"text": text});
26055         var inner = el.getElementsByTagName("em")[0];
26056         return {"el": el, "inner": inner};
26057     }
26058 };/*
26059  * Based on:
26060  * Ext JS Library 1.1.1
26061  * Copyright(c) 2006-2007, Ext JS, LLC.
26062  *
26063  * Originally Released Under LGPL - original licence link has changed is not relivant.
26064  *
26065  * Fork - LGPL
26066  * <script type="text/javascript">
26067  */
26068
26069 /**
26070  * @class Roo.Button
26071  * @extends Roo.util.Observable
26072  * Simple Button class
26073  * @cfg {String} text The button text
26074  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
26075  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
26076  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
26077  * @cfg {Object} scope The scope of the handler
26078  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
26079  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
26080  * @cfg {Boolean} hidden True to start hidden (defaults to false)
26081  * @cfg {Boolean} disabled True to start disabled (defaults to false)
26082  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
26083  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
26084    applies if enableToggle = true)
26085  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
26086  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
26087   an {@link Roo.util.ClickRepeater} config object (defaults to false).
26088  * @constructor
26089  * Create a new button
26090  * @param {Object} config The config object
26091  */
26092 Roo.Button = function(renderTo, config)
26093 {
26094     if (!config) {
26095         config = renderTo;
26096         renderTo = config.renderTo || false;
26097     }
26098     
26099     Roo.apply(this, config);
26100     this.addEvents({
26101         /**
26102              * @event click
26103              * Fires when this button is clicked
26104              * @param {Button} this
26105              * @param {EventObject} e The click event
26106              */
26107             "click" : true,
26108         /**
26109              * @event toggle
26110              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
26111              * @param {Button} this
26112              * @param {Boolean} pressed
26113              */
26114             "toggle" : true,
26115         /**
26116              * @event mouseover
26117              * Fires when the mouse hovers over the button
26118              * @param {Button} this
26119              * @param {Event} e The event object
26120              */
26121         'mouseover' : true,
26122         /**
26123              * @event mouseout
26124              * Fires when the mouse exits the button
26125              * @param {Button} this
26126              * @param {Event} e The event object
26127              */
26128         'mouseout': true,
26129          /**
26130              * @event render
26131              * Fires when the button is rendered
26132              * @param {Button} this
26133              */
26134         'render': true
26135     });
26136     if(this.menu){
26137         this.menu = Roo.menu.MenuMgr.get(this.menu);
26138     }
26139     // register listeners first!!  - so render can be captured..
26140     Roo.util.Observable.call(this);
26141     if(renderTo){
26142         this.render(renderTo);
26143     }
26144     
26145   
26146 };
26147
26148 Roo.extend(Roo.Button, Roo.util.Observable, {
26149     /**
26150      * 
26151      */
26152     
26153     /**
26154      * Read-only. True if this button is hidden
26155      * @type Boolean
26156      */
26157     hidden : false,
26158     /**
26159      * Read-only. True if this button is disabled
26160      * @type Boolean
26161      */
26162     disabled : false,
26163     /**
26164      * Read-only. True if this button is pressed (only if enableToggle = true)
26165      * @type Boolean
26166      */
26167     pressed : false,
26168
26169     /**
26170      * @cfg {Number} tabIndex 
26171      * The DOM tabIndex for this button (defaults to undefined)
26172      */
26173     tabIndex : undefined,
26174
26175     /**
26176      * @cfg {Boolean} enableToggle
26177      * True to enable pressed/not pressed toggling (defaults to false)
26178      */
26179     enableToggle: false,
26180     /**
26181      * @cfg {Mixed} menu
26182      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
26183      */
26184     menu : undefined,
26185     /**
26186      * @cfg {String} menuAlign
26187      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
26188      */
26189     menuAlign : "tl-bl?",
26190
26191     /**
26192      * @cfg {String} iconCls
26193      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
26194      */
26195     iconCls : undefined,
26196     /**
26197      * @cfg {String} type
26198      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
26199      */
26200     type : 'button',
26201
26202     // private
26203     menuClassTarget: 'tr',
26204
26205     /**
26206      * @cfg {String} clickEvent
26207      * The type of event to map to the button's event handler (defaults to 'click')
26208      */
26209     clickEvent : 'click',
26210
26211     /**
26212      * @cfg {Boolean} handleMouseEvents
26213      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
26214      */
26215     handleMouseEvents : true,
26216
26217     /**
26218      * @cfg {String} tooltipType
26219      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
26220      */
26221     tooltipType : 'qtip',
26222
26223     /**
26224      * @cfg {String} cls
26225      * A CSS class to apply to the button's main element.
26226      */
26227     
26228     /**
26229      * @cfg {Roo.Template} template (Optional)
26230      * An {@link Roo.Template} with which to create the Button's main element. This Template must
26231      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
26232      * require code modifications if required elements (e.g. a button) aren't present.
26233      */
26234
26235     // private
26236     render : function(renderTo){
26237         var btn;
26238         if(this.hideParent){
26239             this.parentEl = Roo.get(renderTo);
26240         }
26241         if(!this.dhconfig){
26242             if(!this.template){
26243                 if(!Roo.Button.buttonTemplate){
26244                     // hideous table template
26245                     Roo.Button.buttonTemplate = new Roo.Template(
26246                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
26247                         '<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>',
26248                         "</tr></tbody></table>");
26249                 }
26250                 this.template = Roo.Button.buttonTemplate;
26251             }
26252             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
26253             var btnEl = btn.child("button:first");
26254             btnEl.on('focus', this.onFocus, this);
26255             btnEl.on('blur', this.onBlur, this);
26256             if(this.cls){
26257                 btn.addClass(this.cls);
26258             }
26259             if(this.icon){
26260                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
26261             }
26262             if(this.iconCls){
26263                 btnEl.addClass(this.iconCls);
26264                 if(!this.cls){
26265                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
26266                 }
26267             }
26268             if(this.tabIndex !== undefined){
26269                 btnEl.dom.tabIndex = this.tabIndex;
26270             }
26271             if(this.tooltip){
26272                 if(typeof this.tooltip == 'object'){
26273                     Roo.QuickTips.tips(Roo.apply({
26274                           target: btnEl.id
26275                     }, this.tooltip));
26276                 } else {
26277                     btnEl.dom[this.tooltipType] = this.tooltip;
26278                 }
26279             }
26280         }else{
26281             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
26282         }
26283         this.el = btn;
26284         if(this.id){
26285             this.el.dom.id = this.el.id = this.id;
26286         }
26287         if(this.menu){
26288             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
26289             this.menu.on("show", this.onMenuShow, this);
26290             this.menu.on("hide", this.onMenuHide, this);
26291         }
26292         btn.addClass("x-btn");
26293         if(Roo.isIE && !Roo.isIE7){
26294             this.autoWidth.defer(1, this);
26295         }else{
26296             this.autoWidth();
26297         }
26298         if(this.handleMouseEvents){
26299             btn.on("mouseover", this.onMouseOver, this);
26300             btn.on("mouseout", this.onMouseOut, this);
26301             btn.on("mousedown", this.onMouseDown, this);
26302         }
26303         btn.on(this.clickEvent, this.onClick, this);
26304         //btn.on("mouseup", this.onMouseUp, this);
26305         if(this.hidden){
26306             this.hide();
26307         }
26308         if(this.disabled){
26309             this.disable();
26310         }
26311         Roo.ButtonToggleMgr.register(this);
26312         if(this.pressed){
26313             this.el.addClass("x-btn-pressed");
26314         }
26315         if(this.repeat){
26316             var repeater = new Roo.util.ClickRepeater(btn,
26317                 typeof this.repeat == "object" ? this.repeat : {}
26318             );
26319             repeater.on("click", this.onClick,  this);
26320         }
26321         
26322         this.fireEvent('render', this);
26323         
26324     },
26325     /**
26326      * Returns the button's underlying element
26327      * @return {Roo.Element} The element
26328      */
26329     getEl : function(){
26330         return this.el;  
26331     },
26332     
26333     /**
26334      * Destroys this Button and removes any listeners.
26335      */
26336     destroy : function(){
26337         Roo.ButtonToggleMgr.unregister(this);
26338         this.el.removeAllListeners();
26339         this.purgeListeners();
26340         this.el.remove();
26341     },
26342
26343     // private
26344     autoWidth : function(){
26345         if(this.el){
26346             this.el.setWidth("auto");
26347             if(Roo.isIE7 && Roo.isStrict){
26348                 var ib = this.el.child('button');
26349                 if(ib && ib.getWidth() > 20){
26350                     ib.clip();
26351                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
26352                 }
26353             }
26354             if(this.minWidth){
26355                 if(this.hidden){
26356                     this.el.beginMeasure();
26357                 }
26358                 if(this.el.getWidth() < this.minWidth){
26359                     this.el.setWidth(this.minWidth);
26360                 }
26361                 if(this.hidden){
26362                     this.el.endMeasure();
26363                 }
26364             }
26365         }
26366     },
26367
26368     /**
26369      * Assigns this button's click handler
26370      * @param {Function} handler The function to call when the button is clicked
26371      * @param {Object} scope (optional) Scope for the function passed in
26372      */
26373     setHandler : function(handler, scope){
26374         this.handler = handler;
26375         this.scope = scope;  
26376     },
26377     
26378     /**
26379      * Sets this button's text
26380      * @param {String} text The button text
26381      */
26382     setText : function(text){
26383         this.text = text;
26384         if(this.el){
26385             this.el.child("td.x-btn-center button.x-btn-text").update(text);
26386         }
26387         this.autoWidth();
26388     },
26389     
26390     /**
26391      * Gets the text for this button
26392      * @return {String} The button text
26393      */
26394     getText : function(){
26395         return this.text;  
26396     },
26397     
26398     /**
26399      * Show this button
26400      */
26401     show: function(){
26402         this.hidden = false;
26403         if(this.el){
26404             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
26405         }
26406     },
26407     
26408     /**
26409      * Hide this button
26410      */
26411     hide: function(){
26412         this.hidden = true;
26413         if(this.el){
26414             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
26415         }
26416     },
26417     
26418     /**
26419      * Convenience function for boolean show/hide
26420      * @param {Boolean} visible True to show, false to hide
26421      */
26422     setVisible: function(visible){
26423         if(visible) {
26424             this.show();
26425         }else{
26426             this.hide();
26427         }
26428     },
26429     
26430     /**
26431      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
26432      * @param {Boolean} state (optional) Force a particular state
26433      */
26434     toggle : function(state){
26435         state = state === undefined ? !this.pressed : state;
26436         if(state != this.pressed){
26437             if(state){
26438                 this.el.addClass("x-btn-pressed");
26439                 this.pressed = true;
26440                 this.fireEvent("toggle", this, true);
26441             }else{
26442                 this.el.removeClass("x-btn-pressed");
26443                 this.pressed = false;
26444                 this.fireEvent("toggle", this, false);
26445             }
26446             if(this.toggleHandler){
26447                 this.toggleHandler.call(this.scope || this, this, state);
26448             }
26449         }
26450     },
26451     
26452     /**
26453      * Focus the button
26454      */
26455     focus : function(){
26456         this.el.child('button:first').focus();
26457     },
26458     
26459     /**
26460      * Disable this button
26461      */
26462     disable : function(){
26463         if(this.el){
26464             this.el.addClass("x-btn-disabled");
26465         }
26466         this.disabled = true;
26467     },
26468     
26469     /**
26470      * Enable this button
26471      */
26472     enable : function(){
26473         if(this.el){
26474             this.el.removeClass("x-btn-disabled");
26475         }
26476         this.disabled = false;
26477     },
26478
26479     /**
26480      * Convenience function for boolean enable/disable
26481      * @param {Boolean} enabled True to enable, false to disable
26482      */
26483     setDisabled : function(v){
26484         this[v !== true ? "enable" : "disable"]();
26485     },
26486
26487     // private
26488     onClick : function(e){
26489         if(e){
26490             e.preventDefault();
26491         }
26492         if(e.button != 0){
26493             return;
26494         }
26495         if(!this.disabled){
26496             if(this.enableToggle){
26497                 this.toggle();
26498             }
26499             if(this.menu && !this.menu.isVisible()){
26500                 this.menu.show(this.el, this.menuAlign);
26501             }
26502             this.fireEvent("click", this, e);
26503             if(this.handler){
26504                 this.el.removeClass("x-btn-over");
26505                 this.handler.call(this.scope || this, this, e);
26506             }
26507         }
26508     },
26509     // private
26510     onMouseOver : function(e){
26511         if(!this.disabled){
26512             this.el.addClass("x-btn-over");
26513             this.fireEvent('mouseover', this, e);
26514         }
26515     },
26516     // private
26517     onMouseOut : function(e){
26518         if(!e.within(this.el,  true)){
26519             this.el.removeClass("x-btn-over");
26520             this.fireEvent('mouseout', this, e);
26521         }
26522     },
26523     // private
26524     onFocus : function(e){
26525         if(!this.disabled){
26526             this.el.addClass("x-btn-focus");
26527         }
26528     },
26529     // private
26530     onBlur : function(e){
26531         this.el.removeClass("x-btn-focus");
26532     },
26533     // private
26534     onMouseDown : function(e){
26535         if(!this.disabled && e.button == 0){
26536             this.el.addClass("x-btn-click");
26537             Roo.get(document).on('mouseup', this.onMouseUp, this);
26538         }
26539     },
26540     // private
26541     onMouseUp : function(e){
26542         if(e.button == 0){
26543             this.el.removeClass("x-btn-click");
26544             Roo.get(document).un('mouseup', this.onMouseUp, this);
26545         }
26546     },
26547     // private
26548     onMenuShow : function(e){
26549         this.el.addClass("x-btn-menu-active");
26550     },
26551     // private
26552     onMenuHide : function(e){
26553         this.el.removeClass("x-btn-menu-active");
26554     }   
26555 });
26556
26557 // Private utility class used by Button
26558 Roo.ButtonToggleMgr = function(){
26559    var groups = {};
26560    
26561    function toggleGroup(btn, state){
26562        if(state){
26563            var g = groups[btn.toggleGroup];
26564            for(var i = 0, l = g.length; i < l; i++){
26565                if(g[i] != btn){
26566                    g[i].toggle(false);
26567                }
26568            }
26569        }
26570    }
26571    
26572    return {
26573        register : function(btn){
26574            if(!btn.toggleGroup){
26575                return;
26576            }
26577            var g = groups[btn.toggleGroup];
26578            if(!g){
26579                g = groups[btn.toggleGroup] = [];
26580            }
26581            g.push(btn);
26582            btn.on("toggle", toggleGroup);
26583        },
26584        
26585        unregister : function(btn){
26586            if(!btn.toggleGroup){
26587                return;
26588            }
26589            var g = groups[btn.toggleGroup];
26590            if(g){
26591                g.remove(btn);
26592                btn.un("toggle", toggleGroup);
26593            }
26594        }
26595    };
26596 }();/*
26597  * Based on:
26598  * Ext JS Library 1.1.1
26599  * Copyright(c) 2006-2007, Ext JS, LLC.
26600  *
26601  * Originally Released Under LGPL - original licence link has changed is not relivant.
26602  *
26603  * Fork - LGPL
26604  * <script type="text/javascript">
26605  */
26606  
26607 /**
26608  * @class Roo.SplitButton
26609  * @extends Roo.Button
26610  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
26611  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
26612  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
26613  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
26614  * @cfg {String} arrowTooltip The title attribute of the arrow
26615  * @constructor
26616  * Create a new menu button
26617  * @param {String/HTMLElement/Element} renderTo The element to append the button to
26618  * @param {Object} config The config object
26619  */
26620 Roo.SplitButton = function(renderTo, config){
26621     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
26622     /**
26623      * @event arrowclick
26624      * Fires when this button's arrow is clicked
26625      * @param {SplitButton} this
26626      * @param {EventObject} e The click event
26627      */
26628     this.addEvents({"arrowclick":true});
26629 };
26630
26631 Roo.extend(Roo.SplitButton, Roo.Button, {
26632     render : function(renderTo){
26633         // this is one sweet looking template!
26634         var tpl = new Roo.Template(
26635             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
26636             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
26637             '<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>',
26638             "</tbody></table></td><td>",
26639             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
26640             '<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>',
26641             "</tbody></table></td></tr></table>"
26642         );
26643         var btn = tpl.append(renderTo, [this.text, this.type], true);
26644         var btnEl = btn.child("button");
26645         if(this.cls){
26646             btn.addClass(this.cls);
26647         }
26648         if(this.icon){
26649             btnEl.setStyle('background-image', 'url(' +this.icon +')');
26650         }
26651         if(this.iconCls){
26652             btnEl.addClass(this.iconCls);
26653             if(!this.cls){
26654                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
26655             }
26656         }
26657         this.el = btn;
26658         if(this.handleMouseEvents){
26659             btn.on("mouseover", this.onMouseOver, this);
26660             btn.on("mouseout", this.onMouseOut, this);
26661             btn.on("mousedown", this.onMouseDown, this);
26662             btn.on("mouseup", this.onMouseUp, this);
26663         }
26664         btn.on(this.clickEvent, this.onClick, this);
26665         if(this.tooltip){
26666             if(typeof this.tooltip == 'object'){
26667                 Roo.QuickTips.tips(Roo.apply({
26668                       target: btnEl.id
26669                 }, this.tooltip));
26670             } else {
26671                 btnEl.dom[this.tooltipType] = this.tooltip;
26672             }
26673         }
26674         if(this.arrowTooltip){
26675             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
26676         }
26677         if(this.hidden){
26678             this.hide();
26679         }
26680         if(this.disabled){
26681             this.disable();
26682         }
26683         if(this.pressed){
26684             this.el.addClass("x-btn-pressed");
26685         }
26686         if(Roo.isIE && !Roo.isIE7){
26687             this.autoWidth.defer(1, this);
26688         }else{
26689             this.autoWidth();
26690         }
26691         if(this.menu){
26692             this.menu.on("show", this.onMenuShow, this);
26693             this.menu.on("hide", this.onMenuHide, this);
26694         }
26695         this.fireEvent('render', this);
26696     },
26697
26698     // private
26699     autoWidth : function(){
26700         if(this.el){
26701             var tbl = this.el.child("table:first");
26702             var tbl2 = this.el.child("table:last");
26703             this.el.setWidth("auto");
26704             tbl.setWidth("auto");
26705             if(Roo.isIE7 && Roo.isStrict){
26706                 var ib = this.el.child('button:first');
26707                 if(ib && ib.getWidth() > 20){
26708                     ib.clip();
26709                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
26710                 }
26711             }
26712             if(this.minWidth){
26713                 if(this.hidden){
26714                     this.el.beginMeasure();
26715                 }
26716                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
26717                     tbl.setWidth(this.minWidth-tbl2.getWidth());
26718                 }
26719                 if(this.hidden){
26720                     this.el.endMeasure();
26721                 }
26722             }
26723             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
26724         } 
26725     },
26726     /**
26727      * Sets this button's click handler
26728      * @param {Function} handler The function to call when the button is clicked
26729      * @param {Object} scope (optional) Scope for the function passed above
26730      */
26731     setHandler : function(handler, scope){
26732         this.handler = handler;
26733         this.scope = scope;  
26734     },
26735     
26736     /**
26737      * Sets this button's arrow click handler
26738      * @param {Function} handler The function to call when the arrow is clicked
26739      * @param {Object} scope (optional) Scope for the function passed above
26740      */
26741     setArrowHandler : function(handler, scope){
26742         this.arrowHandler = handler;
26743         this.scope = scope;  
26744     },
26745     
26746     /**
26747      * Focus the button
26748      */
26749     focus : function(){
26750         if(this.el){
26751             this.el.child("button:first").focus();
26752         }
26753     },
26754
26755     // private
26756     onClick : function(e){
26757         e.preventDefault();
26758         if(!this.disabled){
26759             if(e.getTarget(".x-btn-menu-arrow-wrap")){
26760                 if(this.menu && !this.menu.isVisible()){
26761                     this.menu.show(this.el, this.menuAlign);
26762                 }
26763                 this.fireEvent("arrowclick", this, e);
26764                 if(this.arrowHandler){
26765                     this.arrowHandler.call(this.scope || this, this, e);
26766                 }
26767             }else{
26768                 this.fireEvent("click", this, e);
26769                 if(this.handler){
26770                     this.handler.call(this.scope || this, this, e);
26771                 }
26772             }
26773         }
26774     },
26775     // private
26776     onMouseDown : function(e){
26777         if(!this.disabled){
26778             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
26779         }
26780     },
26781     // private
26782     onMouseUp : function(e){
26783         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
26784     }   
26785 });
26786
26787
26788 // backwards compat
26789 Roo.MenuButton = Roo.SplitButton;/*
26790  * Based on:
26791  * Ext JS Library 1.1.1
26792  * Copyright(c) 2006-2007, Ext JS, LLC.
26793  *
26794  * Originally Released Under LGPL - original licence link has changed is not relivant.
26795  *
26796  * Fork - LGPL
26797  * <script type="text/javascript">
26798  */
26799
26800 /**
26801  * @class Roo.Toolbar
26802  * Basic Toolbar class.
26803  * @constructor
26804  * Creates a new Toolbar
26805  * @param {Object} container The config object
26806  */ 
26807 Roo.Toolbar = function(container, buttons, config)
26808 {
26809     /// old consturctor format still supported..
26810     if(container instanceof Array){ // omit the container for later rendering
26811         buttons = container;
26812         config = buttons;
26813         container = null;
26814     }
26815     if (typeof(container) == 'object' && container.xtype) {
26816         config = container;
26817         container = config.container;
26818         buttons = config.buttons || []; // not really - use items!!
26819     }
26820     var xitems = [];
26821     if (config && config.items) {
26822         xitems = config.items;
26823         delete config.items;
26824     }
26825     Roo.apply(this, config);
26826     this.buttons = buttons;
26827     
26828     if(container){
26829         this.render(container);
26830     }
26831     this.xitems = xitems;
26832     Roo.each(xitems, function(b) {
26833         this.add(b);
26834     }, this);
26835     
26836 };
26837
26838 Roo.Toolbar.prototype = {
26839     /**
26840      * @cfg {Array} items
26841      * array of button configs or elements to add (will be converted to a MixedCollection)
26842      */
26843     
26844     /**
26845      * @cfg {String/HTMLElement/Element} container
26846      * The id or element that will contain the toolbar
26847      */
26848     // private
26849     render : function(ct){
26850         this.el = Roo.get(ct);
26851         if(this.cls){
26852             this.el.addClass(this.cls);
26853         }
26854         // using a table allows for vertical alignment
26855         // 100% width is needed by Safari...
26856         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
26857         this.tr = this.el.child("tr", true);
26858         var autoId = 0;
26859         this.items = new Roo.util.MixedCollection(false, function(o){
26860             return o.id || ("item" + (++autoId));
26861         });
26862         if(this.buttons){
26863             this.add.apply(this, this.buttons);
26864             delete this.buttons;
26865         }
26866     },
26867
26868     /**
26869      * Adds element(s) to the toolbar -- this function takes a variable number of 
26870      * arguments of mixed type and adds them to the toolbar.
26871      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
26872      * <ul>
26873      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
26874      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
26875      * <li>Field: Any form field (equivalent to {@link #addField})</li>
26876      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
26877      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
26878      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
26879      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
26880      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
26881      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
26882      * </ul>
26883      * @param {Mixed} arg2
26884      * @param {Mixed} etc.
26885      */
26886     add : function(){
26887         var a = arguments, l = a.length;
26888         for(var i = 0; i < l; i++){
26889             this._add(a[i]);
26890         }
26891     },
26892     // private..
26893     _add : function(el) {
26894         
26895         if (el.xtype) {
26896             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
26897         }
26898         
26899         if (el.applyTo){ // some kind of form field
26900             return this.addField(el);
26901         } 
26902         if (el.render){ // some kind of Toolbar.Item
26903             return this.addItem(el);
26904         }
26905         if (typeof el == "string"){ // string
26906             if(el == "separator" || el == "-"){
26907                 return this.addSeparator();
26908             }
26909             if (el == " "){
26910                 return this.addSpacer();
26911             }
26912             if(el == "->"){
26913                 return this.addFill();
26914             }
26915             return this.addText(el);
26916             
26917         }
26918         if(el.tagName){ // element
26919             return this.addElement(el);
26920         }
26921         if(typeof el == "object"){ // must be button config?
26922             return this.addButton(el);
26923         }
26924         // and now what?!?!
26925         return false;
26926         
26927     },
26928     
26929     /**
26930      * Add an Xtype element
26931      * @param {Object} xtype Xtype Object
26932      * @return {Object} created Object
26933      */
26934     addxtype : function(e){
26935         return this.add(e);  
26936     },
26937     
26938     /**
26939      * Returns the Element for this toolbar.
26940      * @return {Roo.Element}
26941      */
26942     getEl : function(){
26943         return this.el;  
26944     },
26945     
26946     /**
26947      * Adds a separator
26948      * @return {Roo.Toolbar.Item} The separator item
26949      */
26950     addSeparator : function(){
26951         return this.addItem(new Roo.Toolbar.Separator());
26952     },
26953
26954     /**
26955      * Adds a spacer element
26956      * @return {Roo.Toolbar.Spacer} The spacer item
26957      */
26958     addSpacer : function(){
26959         return this.addItem(new Roo.Toolbar.Spacer());
26960     },
26961
26962     /**
26963      * Adds a fill element that forces subsequent additions to the right side of the toolbar
26964      * @return {Roo.Toolbar.Fill} The fill item
26965      */
26966     addFill : function(){
26967         return this.addItem(new Roo.Toolbar.Fill());
26968     },
26969
26970     /**
26971      * Adds any standard HTML element to the toolbar
26972      * @param {String/HTMLElement/Element} el The element or id of the element to add
26973      * @return {Roo.Toolbar.Item} The element's item
26974      */
26975     addElement : function(el){
26976         return this.addItem(new Roo.Toolbar.Item(el));
26977     },
26978     /**
26979      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
26980      * @type Roo.util.MixedCollection  
26981      */
26982     items : false,
26983      
26984     /**
26985      * Adds any Toolbar.Item or subclass
26986      * @param {Roo.Toolbar.Item} item
26987      * @return {Roo.Toolbar.Item} The item
26988      */
26989     addItem : function(item){
26990         var td = this.nextBlock();
26991         item.render(td);
26992         this.items.add(item);
26993         return item;
26994     },
26995     
26996     /**
26997      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
26998      * @param {Object/Array} config A button config or array of configs
26999      * @return {Roo.Toolbar.Button/Array}
27000      */
27001     addButton : function(config){
27002         if(config instanceof Array){
27003             var buttons = [];
27004             for(var i = 0, len = config.length; i < len; i++) {
27005                 buttons.push(this.addButton(config[i]));
27006             }
27007             return buttons;
27008         }
27009         var b = config;
27010         if(!(config instanceof Roo.Toolbar.Button)){
27011             b = config.split ?
27012                 new Roo.Toolbar.SplitButton(config) :
27013                 new Roo.Toolbar.Button(config);
27014         }
27015         var td = this.nextBlock();
27016         b.render(td);
27017         this.items.add(b);
27018         return b;
27019     },
27020     
27021     /**
27022      * Adds text to the toolbar
27023      * @param {String} text The text to add
27024      * @return {Roo.Toolbar.Item} The element's item
27025      */
27026     addText : function(text){
27027         return this.addItem(new Roo.Toolbar.TextItem(text));
27028     },
27029     
27030     /**
27031      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
27032      * @param {Number} index The index where the item is to be inserted
27033      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
27034      * @return {Roo.Toolbar.Button/Item}
27035      */
27036     insertButton : function(index, item){
27037         if(item instanceof Array){
27038             var buttons = [];
27039             for(var i = 0, len = item.length; i < len; i++) {
27040                buttons.push(this.insertButton(index + i, item[i]));
27041             }
27042             return buttons;
27043         }
27044         if (!(item instanceof Roo.Toolbar.Button)){
27045            item = new Roo.Toolbar.Button(item);
27046         }
27047         var td = document.createElement("td");
27048         this.tr.insertBefore(td, this.tr.childNodes[index]);
27049         item.render(td);
27050         this.items.insert(index, item);
27051         return item;
27052     },
27053     
27054     /**
27055      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
27056      * @param {Object} config
27057      * @return {Roo.Toolbar.Item} The element's item
27058      */
27059     addDom : function(config, returnEl){
27060         var td = this.nextBlock();
27061         Roo.DomHelper.overwrite(td, config);
27062         var ti = new Roo.Toolbar.Item(td.firstChild);
27063         ti.render(td);
27064         this.items.add(ti);
27065         return ti;
27066     },
27067
27068     /**
27069      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
27070      * @type Roo.util.MixedCollection  
27071      */
27072     fields : false,
27073     
27074     /**
27075      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
27076      * Note: the field should not have been rendered yet. For a field that has already been
27077      * rendered, use {@link #addElement}.
27078      * @param {Roo.form.Field} field
27079      * @return {Roo.ToolbarItem}
27080      */
27081      
27082       
27083     addField : function(field) {
27084         if (!this.fields) {
27085             var autoId = 0;
27086             this.fields = new Roo.util.MixedCollection(false, function(o){
27087                 return o.id || ("item" + (++autoId));
27088             });
27089
27090         }
27091         
27092         var td = this.nextBlock();
27093         field.render(td);
27094         var ti = new Roo.Toolbar.Item(td.firstChild);
27095         ti.render(td);
27096         this.items.add(ti);
27097         this.fields.add(field);
27098         return ti;
27099     },
27100     /**
27101      * Hide the toolbar
27102      * @method hide
27103      */
27104      
27105       
27106     hide : function()
27107     {
27108         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
27109         this.el.child('div').hide();
27110     },
27111     /**
27112      * Show the toolbar
27113      * @method show
27114      */
27115     show : function()
27116     {
27117         this.el.child('div').show();
27118     },
27119       
27120     // private
27121     nextBlock : function(){
27122         var td = document.createElement("td");
27123         this.tr.appendChild(td);
27124         return td;
27125     },
27126
27127     // private
27128     destroy : function(){
27129         if(this.items){ // rendered?
27130             Roo.destroy.apply(Roo, this.items.items);
27131         }
27132         if(this.fields){ // rendered?
27133             Roo.destroy.apply(Roo, this.fields.items);
27134         }
27135         Roo.Element.uncache(this.el, this.tr);
27136     }
27137 };
27138
27139 /**
27140  * @class Roo.Toolbar.Item
27141  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
27142  * @constructor
27143  * Creates a new Item
27144  * @param {HTMLElement} el 
27145  */
27146 Roo.Toolbar.Item = function(el){
27147     this.el = Roo.getDom(el);
27148     this.id = Roo.id(this.el);
27149     this.hidden = false;
27150 };
27151
27152 Roo.Toolbar.Item.prototype = {
27153     
27154     /**
27155      * Get this item's HTML Element
27156      * @return {HTMLElement}
27157      */
27158     getEl : function(){
27159        return this.el;  
27160     },
27161
27162     // private
27163     render : function(td){
27164         this.td = td;
27165         td.appendChild(this.el);
27166     },
27167     
27168     /**
27169      * Removes and destroys this item.
27170      */
27171     destroy : function(){
27172         this.td.parentNode.removeChild(this.td);
27173     },
27174     
27175     /**
27176      * Shows this item.
27177      */
27178     show: function(){
27179         this.hidden = false;
27180         this.td.style.display = "";
27181     },
27182     
27183     /**
27184      * Hides this item.
27185      */
27186     hide: function(){
27187         this.hidden = true;
27188         this.td.style.display = "none";
27189     },
27190     
27191     /**
27192      * Convenience function for boolean show/hide.
27193      * @param {Boolean} visible true to show/false to hide
27194      */
27195     setVisible: function(visible){
27196         if(visible) {
27197             this.show();
27198         }else{
27199             this.hide();
27200         }
27201     },
27202     
27203     /**
27204      * Try to focus this item.
27205      */
27206     focus : function(){
27207         Roo.fly(this.el).focus();
27208     },
27209     
27210     /**
27211      * Disables this item.
27212      */
27213     disable : function(){
27214         Roo.fly(this.td).addClass("x-item-disabled");
27215         this.disabled = true;
27216         this.el.disabled = true;
27217     },
27218     
27219     /**
27220      * Enables this item.
27221      */
27222     enable : function(){
27223         Roo.fly(this.td).removeClass("x-item-disabled");
27224         this.disabled = false;
27225         this.el.disabled = false;
27226     }
27227 };
27228
27229
27230 /**
27231  * @class Roo.Toolbar.Separator
27232  * @extends Roo.Toolbar.Item
27233  * A simple toolbar separator class
27234  * @constructor
27235  * Creates a new Separator
27236  */
27237 Roo.Toolbar.Separator = function(){
27238     var s = document.createElement("span");
27239     s.className = "ytb-sep";
27240     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
27241 };
27242 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
27243     enable:Roo.emptyFn,
27244     disable:Roo.emptyFn,
27245     focus:Roo.emptyFn
27246 });
27247
27248 /**
27249  * @class Roo.Toolbar.Spacer
27250  * @extends Roo.Toolbar.Item
27251  * A simple element that adds extra horizontal space to a toolbar.
27252  * @constructor
27253  * Creates a new Spacer
27254  */
27255 Roo.Toolbar.Spacer = function(){
27256     var s = document.createElement("div");
27257     s.className = "ytb-spacer";
27258     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
27259 };
27260 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
27261     enable:Roo.emptyFn,
27262     disable:Roo.emptyFn,
27263     focus:Roo.emptyFn
27264 });
27265
27266 /**
27267  * @class Roo.Toolbar.Fill
27268  * @extends Roo.Toolbar.Spacer
27269  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
27270  * @constructor
27271  * Creates a new Spacer
27272  */
27273 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
27274     // private
27275     render : function(td){
27276         td.style.width = '100%';
27277         Roo.Toolbar.Fill.superclass.render.call(this, td);
27278     }
27279 });
27280
27281 /**
27282  * @class Roo.Toolbar.TextItem
27283  * @extends Roo.Toolbar.Item
27284  * A simple class that renders text directly into a toolbar.
27285  * @constructor
27286  * Creates a new TextItem
27287  * @param {String} text
27288  */
27289 Roo.Toolbar.TextItem = function(text){
27290     if (typeof(text) == 'object') {
27291         text = text.text;
27292     }
27293     var s = document.createElement("span");
27294     s.className = "ytb-text";
27295     s.innerHTML = text;
27296     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
27297 };
27298 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
27299     enable:Roo.emptyFn,
27300     disable:Roo.emptyFn,
27301     focus:Roo.emptyFn
27302 });
27303
27304 /**
27305  * @class Roo.Toolbar.Button
27306  * @extends Roo.Button
27307  * A button that renders into a toolbar.
27308  * @constructor
27309  * Creates a new Button
27310  * @param {Object} config A standard {@link Roo.Button} config object
27311  */
27312 Roo.Toolbar.Button = function(config){
27313     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
27314 };
27315 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
27316     render : function(td){
27317         this.td = td;
27318         Roo.Toolbar.Button.superclass.render.call(this, td);
27319     },
27320     
27321     /**
27322      * Removes and destroys this button
27323      */
27324     destroy : function(){
27325         Roo.Toolbar.Button.superclass.destroy.call(this);
27326         this.td.parentNode.removeChild(this.td);
27327     },
27328     
27329     /**
27330      * Shows this button
27331      */
27332     show: function(){
27333         this.hidden = false;
27334         this.td.style.display = "";
27335     },
27336     
27337     /**
27338      * Hides this button
27339      */
27340     hide: function(){
27341         this.hidden = true;
27342         this.td.style.display = "none";
27343     },
27344
27345     /**
27346      * Disables this item
27347      */
27348     disable : function(){
27349         Roo.fly(this.td).addClass("x-item-disabled");
27350         this.disabled = true;
27351     },
27352
27353     /**
27354      * Enables this item
27355      */
27356     enable : function(){
27357         Roo.fly(this.td).removeClass("x-item-disabled");
27358         this.disabled = false;
27359     }
27360 });
27361 // backwards compat
27362 Roo.ToolbarButton = Roo.Toolbar.Button;
27363
27364 /**
27365  * @class Roo.Toolbar.SplitButton
27366  * @extends Roo.SplitButton
27367  * A menu button that renders into a toolbar.
27368  * @constructor
27369  * Creates a new SplitButton
27370  * @param {Object} config A standard {@link Roo.SplitButton} config object
27371  */
27372 Roo.Toolbar.SplitButton = function(config){
27373     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
27374 };
27375 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
27376     render : function(td){
27377         this.td = td;
27378         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
27379     },
27380     
27381     /**
27382      * Removes and destroys this button
27383      */
27384     destroy : function(){
27385         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
27386         this.td.parentNode.removeChild(this.td);
27387     },
27388     
27389     /**
27390      * Shows this button
27391      */
27392     show: function(){
27393         this.hidden = false;
27394         this.td.style.display = "";
27395     },
27396     
27397     /**
27398      * Hides this button
27399      */
27400     hide: function(){
27401         this.hidden = true;
27402         this.td.style.display = "none";
27403     }
27404 });
27405
27406 // backwards compat
27407 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
27408  * Based on:
27409  * Ext JS Library 1.1.1
27410  * Copyright(c) 2006-2007, Ext JS, LLC.
27411  *
27412  * Originally Released Under LGPL - original licence link has changed is not relivant.
27413  *
27414  * Fork - LGPL
27415  * <script type="text/javascript">
27416  */
27417  
27418 /**
27419  * @class Roo.PagingToolbar
27420  * @extends Roo.Toolbar
27421  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27422  * @constructor
27423  * Create a new PagingToolbar
27424  * @param {Object} config The config object
27425  */
27426 Roo.PagingToolbar = function(el, ds, config)
27427 {
27428     // old args format still supported... - xtype is prefered..
27429     if (typeof(el) == 'object' && el.xtype) {
27430         // created from xtype...
27431         config = el;
27432         ds = el.dataSource;
27433         el = config.container;
27434     }
27435     var items = [];
27436     if (config.items) {
27437         items = config.items;
27438         config.items = [];
27439     }
27440     
27441     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
27442     this.ds = ds;
27443     this.cursor = 0;
27444     this.renderButtons(this.el);
27445     this.bind(ds);
27446     
27447     // supprot items array.
27448    
27449     Roo.each(items, function(e) {
27450         this.add(Roo.factory(e));
27451     },this);
27452     
27453 };
27454
27455 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
27456     /**
27457      * @cfg {Roo.data.Store} dataSource
27458      * The underlying data store providing the paged data
27459      */
27460     /**
27461      * @cfg {String/HTMLElement/Element} container
27462      * container The id or element that will contain the toolbar
27463      */
27464     /**
27465      * @cfg {Boolean} displayInfo
27466      * True to display the displayMsg (defaults to false)
27467      */
27468     /**
27469      * @cfg {Number} pageSize
27470      * The number of records to display per page (defaults to 20)
27471      */
27472     pageSize: 20,
27473     /**
27474      * @cfg {String} displayMsg
27475      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27476      */
27477     displayMsg : 'Displaying {0} - {1} of {2}',
27478     /**
27479      * @cfg {String} emptyMsg
27480      * The message to display when no records are found (defaults to "No data to display")
27481      */
27482     emptyMsg : 'No data to display',
27483     /**
27484      * Customizable piece of the default paging text (defaults to "Page")
27485      * @type String
27486      */
27487     beforePageText : "Page",
27488     /**
27489      * Customizable piece of the default paging text (defaults to "of %0")
27490      * @type String
27491      */
27492     afterPageText : "of {0}",
27493     /**
27494      * Customizable piece of the default paging text (defaults to "First Page")
27495      * @type String
27496      */
27497     firstText : "First Page",
27498     /**
27499      * Customizable piece of the default paging text (defaults to "Previous Page")
27500      * @type String
27501      */
27502     prevText : "Previous Page",
27503     /**
27504      * Customizable piece of the default paging text (defaults to "Next Page")
27505      * @type String
27506      */
27507     nextText : "Next Page",
27508     /**
27509      * Customizable piece of the default paging text (defaults to "Last Page")
27510      * @type String
27511      */
27512     lastText : "Last Page",
27513     /**
27514      * Customizable piece of the default paging text (defaults to "Refresh")
27515      * @type String
27516      */
27517     refreshText : "Refresh",
27518
27519     // private
27520     renderButtons : function(el){
27521         Roo.PagingToolbar.superclass.render.call(this, el);
27522         this.first = this.addButton({
27523             tooltip: this.firstText,
27524             cls: "x-btn-icon x-grid-page-first",
27525             disabled: true,
27526             handler: this.onClick.createDelegate(this, ["first"])
27527         });
27528         this.prev = this.addButton({
27529             tooltip: this.prevText,
27530             cls: "x-btn-icon x-grid-page-prev",
27531             disabled: true,
27532             handler: this.onClick.createDelegate(this, ["prev"])
27533         });
27534         //this.addSeparator();
27535         this.add(this.beforePageText);
27536         this.field = Roo.get(this.addDom({
27537            tag: "input",
27538            type: "text",
27539            size: "3",
27540            value: "1",
27541            cls: "x-grid-page-number"
27542         }).el);
27543         this.field.on("keydown", this.onPagingKeydown, this);
27544         this.field.on("focus", function(){this.dom.select();});
27545         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
27546         this.field.setHeight(18);
27547         //this.addSeparator();
27548         this.next = this.addButton({
27549             tooltip: this.nextText,
27550             cls: "x-btn-icon x-grid-page-next",
27551             disabled: true,
27552             handler: this.onClick.createDelegate(this, ["next"])
27553         });
27554         this.last = this.addButton({
27555             tooltip: this.lastText,
27556             cls: "x-btn-icon x-grid-page-last",
27557             disabled: true,
27558             handler: this.onClick.createDelegate(this, ["last"])
27559         });
27560         //this.addSeparator();
27561         this.loading = this.addButton({
27562             tooltip: this.refreshText,
27563             cls: "x-btn-icon x-grid-loading",
27564             handler: this.onClick.createDelegate(this, ["refresh"])
27565         });
27566
27567         if(this.displayInfo){
27568             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
27569         }
27570     },
27571
27572     // private
27573     updateInfo : function(){
27574         if(this.displayEl){
27575             var count = this.ds.getCount();
27576             var msg = count == 0 ?
27577                 this.emptyMsg :
27578                 String.format(
27579                     this.displayMsg,
27580                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27581                 );
27582             this.displayEl.update(msg);
27583         }
27584     },
27585
27586     // private
27587     onLoad : function(ds, r, o){
27588        this.cursor = o.params ? o.params.start : 0;
27589        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
27590
27591        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
27592        this.field.dom.value = ap;
27593        this.first.setDisabled(ap == 1);
27594        this.prev.setDisabled(ap == 1);
27595        this.next.setDisabled(ap == ps);
27596        this.last.setDisabled(ap == ps);
27597        this.loading.enable();
27598        this.updateInfo();
27599     },
27600
27601     // private
27602     getPageData : function(){
27603         var total = this.ds.getTotalCount();
27604         return {
27605             total : total,
27606             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27607             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27608         };
27609     },
27610
27611     // private
27612     onLoadError : function(){
27613         this.loading.enable();
27614     },
27615
27616     // private
27617     onPagingKeydown : function(e){
27618         var k = e.getKey();
27619         var d = this.getPageData();
27620         if(k == e.RETURN){
27621             var v = this.field.dom.value, pageNum;
27622             if(!v || isNaN(pageNum = parseInt(v, 10))){
27623                 this.field.dom.value = d.activePage;
27624                 return;
27625             }
27626             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27627             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27628             e.stopEvent();
27629         }
27630         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))
27631         {
27632           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27633           this.field.dom.value = pageNum;
27634           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27635           e.stopEvent();
27636         }
27637         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27638         {
27639           var v = this.field.dom.value, pageNum; 
27640           var increment = (e.shiftKey) ? 10 : 1;
27641           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27642             increment *= -1;
27643           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27644             this.field.dom.value = d.activePage;
27645             return;
27646           }
27647           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27648           {
27649             this.field.dom.value = parseInt(v, 10) + increment;
27650             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27651             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27652           }
27653           e.stopEvent();
27654         }
27655     },
27656
27657     // private
27658     beforeLoad : function(){
27659         if(this.loading){
27660             this.loading.disable();
27661         }
27662     },
27663
27664     // private
27665     onClick : function(which){
27666         var ds = this.ds;
27667         switch(which){
27668             case "first":
27669                 ds.load({params:{start: 0, limit: this.pageSize}});
27670             break;
27671             case "prev":
27672                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27673             break;
27674             case "next":
27675                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27676             break;
27677             case "last":
27678                 var total = ds.getTotalCount();
27679                 var extra = total % this.pageSize;
27680                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27681                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27682             break;
27683             case "refresh":
27684                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27685             break;
27686         }
27687     },
27688
27689     /**
27690      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27691      * @param {Roo.data.Store} store The data store to unbind
27692      */
27693     unbind : function(ds){
27694         ds.un("beforeload", this.beforeLoad, this);
27695         ds.un("load", this.onLoad, this);
27696         ds.un("loadexception", this.onLoadError, this);
27697         ds.un("remove", this.updateInfo, this);
27698         ds.un("add", this.updateInfo, this);
27699         this.ds = undefined;
27700     },
27701
27702     /**
27703      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27704      * @param {Roo.data.Store} store The data store to bind
27705      */
27706     bind : function(ds){
27707         ds.on("beforeload", this.beforeLoad, this);
27708         ds.on("load", this.onLoad, this);
27709         ds.on("loadexception", this.onLoadError, this);
27710         ds.on("remove", this.updateInfo, this);
27711         ds.on("add", this.updateInfo, this);
27712         this.ds = ds;
27713     }
27714 });/*
27715  * Based on:
27716  * Ext JS Library 1.1.1
27717  * Copyright(c) 2006-2007, Ext JS, LLC.
27718  *
27719  * Originally Released Under LGPL - original licence link has changed is not relivant.
27720  *
27721  * Fork - LGPL
27722  * <script type="text/javascript">
27723  */
27724
27725 /**
27726  * @class Roo.Resizable
27727  * @extends Roo.util.Observable
27728  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
27729  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
27730  * 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
27731  * the element will be wrapped for you automatically.</p>
27732  * <p>Here is the list of valid resize handles:</p>
27733  * <pre>
27734 Value   Description
27735 ------  -------------------
27736  'n'     north
27737  's'     south
27738  'e'     east
27739  'w'     west
27740  'nw'    northwest
27741  'sw'    southwest
27742  'se'    southeast
27743  'ne'    northeast
27744  'hd'    horizontal drag
27745  'all'   all
27746 </pre>
27747  * <p>Here's an example showing the creation of a typical Resizable:</p>
27748  * <pre><code>
27749 var resizer = new Roo.Resizable("element-id", {
27750     handles: 'all',
27751     minWidth: 200,
27752     minHeight: 100,
27753     maxWidth: 500,
27754     maxHeight: 400,
27755     pinned: true
27756 });
27757 resizer.on("resize", myHandler);
27758 </code></pre>
27759  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
27760  * resizer.east.setDisplayed(false);</p>
27761  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
27762  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
27763  * resize operation's new size (defaults to [0, 0])
27764  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
27765  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
27766  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
27767  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
27768  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
27769  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
27770  * @cfg {Number} width The width of the element in pixels (defaults to null)
27771  * @cfg {Number} height The height of the element in pixels (defaults to null)
27772  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
27773  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
27774  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
27775  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
27776  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
27777  * in favor of the handles config option (defaults to false)
27778  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
27779  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
27780  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
27781  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
27782  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
27783  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
27784  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
27785  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
27786  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
27787  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
27788  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
27789  * @constructor
27790  * Create a new resizable component
27791  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
27792  * @param {Object} config configuration options
27793   */
27794 Roo.Resizable = function(el, config)
27795 {
27796     this.el = Roo.get(el);
27797
27798     if(config && config.wrap){
27799         config.resizeChild = this.el;
27800         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
27801         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
27802         this.el.setStyle("overflow", "hidden");
27803         this.el.setPositioning(config.resizeChild.getPositioning());
27804         config.resizeChild.clearPositioning();
27805         if(!config.width || !config.height){
27806             var csize = config.resizeChild.getSize();
27807             this.el.setSize(csize.width, csize.height);
27808         }
27809         if(config.pinned && !config.adjustments){
27810             config.adjustments = "auto";
27811         }
27812     }
27813
27814     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
27815     this.proxy.unselectable();
27816     this.proxy.enableDisplayMode('block');
27817
27818     Roo.apply(this, config);
27819
27820     if(this.pinned){
27821         this.disableTrackOver = true;
27822         this.el.addClass("x-resizable-pinned");
27823     }
27824     // if the element isn't positioned, make it relative
27825     var position = this.el.getStyle("position");
27826     if(position != "absolute" && position != "fixed"){
27827         this.el.setStyle("position", "relative");
27828     }
27829     if(!this.handles){ // no handles passed, must be legacy style
27830         this.handles = 's,e,se';
27831         if(this.multiDirectional){
27832             this.handles += ',n,w';
27833         }
27834     }
27835     if(this.handles == "all"){
27836         this.handles = "n s e w ne nw se sw";
27837     }
27838     var hs = this.handles.split(/\s*?[,;]\s*?| /);
27839     var ps = Roo.Resizable.positions;
27840     for(var i = 0, len = hs.length; i < len; i++){
27841         if(hs[i] && ps[hs[i]]){
27842             var pos = ps[hs[i]];
27843             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
27844         }
27845     }
27846     // legacy
27847     this.corner = this.southeast;
27848     
27849     // updateBox = the box can move..
27850     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
27851         this.updateBox = true;
27852     }
27853
27854     this.activeHandle = null;
27855
27856     if(this.resizeChild){
27857         if(typeof this.resizeChild == "boolean"){
27858             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
27859         }else{
27860             this.resizeChild = Roo.get(this.resizeChild, true);
27861         }
27862     }
27863     
27864     if(this.adjustments == "auto"){
27865         var rc = this.resizeChild;
27866         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
27867         if(rc && (hw || hn)){
27868             rc.position("relative");
27869             rc.setLeft(hw ? hw.el.getWidth() : 0);
27870             rc.setTop(hn ? hn.el.getHeight() : 0);
27871         }
27872         this.adjustments = [
27873             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
27874             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
27875         ];
27876     }
27877
27878     if(this.draggable){
27879         this.dd = this.dynamic ?
27880             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
27881         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
27882     }
27883
27884     // public events
27885     this.addEvents({
27886         /**
27887          * @event beforeresize
27888          * Fired before resize is allowed. Set enabled to false to cancel resize.
27889          * @param {Roo.Resizable} this
27890          * @param {Roo.EventObject} e The mousedown event
27891          */
27892         "beforeresize" : true,
27893         /**
27894          * @event resize
27895          * Fired after a resize.
27896          * @param {Roo.Resizable} this
27897          * @param {Number} width The new width
27898          * @param {Number} height The new height
27899          * @param {Roo.EventObject} e The mouseup event
27900          */
27901         "resize" : true
27902     });
27903
27904     if(this.width !== null && this.height !== null){
27905         this.resizeTo(this.width, this.height);
27906     }else{
27907         this.updateChildSize();
27908     }
27909     if(Roo.isIE){
27910         this.el.dom.style.zoom = 1;
27911     }
27912     Roo.Resizable.superclass.constructor.call(this);
27913 };
27914
27915 Roo.extend(Roo.Resizable, Roo.util.Observable, {
27916         resizeChild : false,
27917         adjustments : [0, 0],
27918         minWidth : 5,
27919         minHeight : 5,
27920         maxWidth : 10000,
27921         maxHeight : 10000,
27922         enabled : true,
27923         animate : false,
27924         duration : .35,
27925         dynamic : false,
27926         handles : false,
27927         multiDirectional : false,
27928         disableTrackOver : false,
27929         easing : 'easeOutStrong',
27930         widthIncrement : 0,
27931         heightIncrement : 0,
27932         pinned : false,
27933         width : null,
27934         height : null,
27935         preserveRatio : false,
27936         transparent: false,
27937         minX: 0,
27938         minY: 0,
27939         draggable: false,
27940
27941         /**
27942          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
27943          */
27944         constrainTo: undefined,
27945         /**
27946          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
27947          */
27948         resizeRegion: undefined,
27949
27950
27951     /**
27952      * Perform a manual resize
27953      * @param {Number} width
27954      * @param {Number} height
27955      */
27956     resizeTo : function(width, height){
27957         this.el.setSize(width, height);
27958         this.updateChildSize();
27959         this.fireEvent("resize", this, width, height, null);
27960     },
27961
27962     // private
27963     startSizing : function(e, handle){
27964         this.fireEvent("beforeresize", this, e);
27965         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
27966
27967             if(!this.overlay){
27968                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
27969                 this.overlay.unselectable();
27970                 this.overlay.enableDisplayMode("block");
27971                 this.overlay.on("mousemove", this.onMouseMove, this);
27972                 this.overlay.on("mouseup", this.onMouseUp, this);
27973             }
27974             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
27975
27976             this.resizing = true;
27977             this.startBox = this.el.getBox();
27978             this.startPoint = e.getXY();
27979             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
27980                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
27981
27982             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
27983             this.overlay.show();
27984
27985             if(this.constrainTo) {
27986                 var ct = Roo.get(this.constrainTo);
27987                 this.resizeRegion = ct.getRegion().adjust(
27988                     ct.getFrameWidth('t'),
27989                     ct.getFrameWidth('l'),
27990                     -ct.getFrameWidth('b'),
27991                     -ct.getFrameWidth('r')
27992                 );
27993             }
27994
27995             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
27996             this.proxy.show();
27997             this.proxy.setBox(this.startBox);
27998             if(!this.dynamic){
27999                 this.proxy.setStyle('visibility', 'visible');
28000             }
28001         }
28002     },
28003
28004     // private
28005     onMouseDown : function(handle, e){
28006         if(this.enabled){
28007             e.stopEvent();
28008             this.activeHandle = handle;
28009             this.startSizing(e, handle);
28010         }
28011     },
28012
28013     // private
28014     onMouseUp : function(e){
28015         var size = this.resizeElement();
28016         this.resizing = false;
28017         this.handleOut();
28018         this.overlay.hide();
28019         this.proxy.hide();
28020         this.fireEvent("resize", this, size.width, size.height, e);
28021     },
28022
28023     // private
28024     updateChildSize : function(){
28025         if(this.resizeChild){
28026             var el = this.el;
28027             var child = this.resizeChild;
28028             var adj = this.adjustments;
28029             if(el.dom.offsetWidth){
28030                 var b = el.getSize(true);
28031                 child.setSize(b.width+adj[0], b.height+adj[1]);
28032             }
28033             // Second call here for IE
28034             // The first call enables instant resizing and
28035             // the second call corrects scroll bars if they
28036             // exist
28037             if(Roo.isIE){
28038                 setTimeout(function(){
28039                     if(el.dom.offsetWidth){
28040                         var b = el.getSize(true);
28041                         child.setSize(b.width+adj[0], b.height+adj[1]);
28042                     }
28043                 }, 10);
28044             }
28045         }
28046     },
28047
28048     // private
28049     snap : function(value, inc, min){
28050         if(!inc || !value) return value;
28051         var newValue = value;
28052         var m = value % inc;
28053         if(m > 0){
28054             if(m > (inc/2)){
28055                 newValue = value + (inc-m);
28056             }else{
28057                 newValue = value - m;
28058             }
28059         }
28060         return Math.max(min, newValue);
28061     },
28062
28063     // private
28064     resizeElement : function(){
28065         var box = this.proxy.getBox();
28066         if(this.updateBox){
28067             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
28068         }else{
28069             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
28070         }
28071         this.updateChildSize();
28072         if(!this.dynamic){
28073             this.proxy.hide();
28074         }
28075         return box;
28076     },
28077
28078     // private
28079     constrain : function(v, diff, m, mx){
28080         if(v - diff < m){
28081             diff = v - m;
28082         }else if(v - diff > mx){
28083             diff = mx - v;
28084         }
28085         return diff;
28086     },
28087
28088     // private
28089     onMouseMove : function(e){
28090         if(this.enabled){
28091             try{// try catch so if something goes wrong the user doesn't get hung
28092
28093             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
28094                 return;
28095             }
28096
28097             //var curXY = this.startPoint;
28098             var curSize = this.curSize || this.startBox;
28099             var x = this.startBox.x, y = this.startBox.y;
28100             var ox = x, oy = y;
28101             var w = curSize.width, h = curSize.height;
28102             var ow = w, oh = h;
28103             var mw = this.minWidth, mh = this.minHeight;
28104             var mxw = this.maxWidth, mxh = this.maxHeight;
28105             var wi = this.widthIncrement;
28106             var hi = this.heightIncrement;
28107
28108             var eventXY = e.getXY();
28109             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
28110             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
28111
28112             var pos = this.activeHandle.position;
28113
28114             switch(pos){
28115                 case "east":
28116                     w += diffX;
28117                     w = Math.min(Math.max(mw, w), mxw);
28118                     break;
28119              
28120                 case "south":
28121                     h += diffY;
28122                     h = Math.min(Math.max(mh, h), mxh);
28123                     break;
28124                 case "southeast":
28125                     w += diffX;
28126                     h += diffY;
28127                     w = Math.min(Math.max(mw, w), mxw);
28128                     h = Math.min(Math.max(mh, h), mxh);
28129                     break;
28130                 case "north":
28131                     diffY = this.constrain(h, diffY, mh, mxh);
28132                     y += diffY;
28133                     h -= diffY;
28134                     break;
28135                 case "hdrag":
28136                     
28137                     if (wi) {
28138                         var adiffX = Math.abs(diffX);
28139                         var sub = (adiffX % wi); // how much 
28140                         if (sub > (wi/2)) { // far enough to snap
28141                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
28142                         } else {
28143                             // remove difference.. 
28144                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
28145                         }
28146                     }
28147                     x += diffX;
28148                     x = Math.max(this.minX, x);
28149                     break;
28150                 case "west":
28151                     diffX = this.constrain(w, diffX, mw, mxw);
28152                     x += diffX;
28153                     w -= diffX;
28154                     break;
28155                 case "northeast":
28156                     w += diffX;
28157                     w = Math.min(Math.max(mw, w), mxw);
28158                     diffY = this.constrain(h, diffY, mh, mxh);
28159                     y += diffY;
28160                     h -= diffY;
28161                     break;
28162                 case "northwest":
28163                     diffX = this.constrain(w, diffX, mw, mxw);
28164                     diffY = this.constrain(h, diffY, mh, mxh);
28165                     y += diffY;
28166                     h -= diffY;
28167                     x += diffX;
28168                     w -= diffX;
28169                     break;
28170                case "southwest":
28171                     diffX = this.constrain(w, diffX, mw, mxw);
28172                     h += diffY;
28173                     h = Math.min(Math.max(mh, h), mxh);
28174                     x += diffX;
28175                     w -= diffX;
28176                     break;
28177             }
28178
28179             var sw = this.snap(w, wi, mw);
28180             var sh = this.snap(h, hi, mh);
28181             if(sw != w || sh != h){
28182                 switch(pos){
28183                     case "northeast":
28184                         y -= sh - h;
28185                     break;
28186                     case "north":
28187                         y -= sh - h;
28188                         break;
28189                     case "southwest":
28190                         x -= sw - w;
28191                     break;
28192                     case "west":
28193                         x -= sw - w;
28194                         break;
28195                     case "northwest":
28196                         x -= sw - w;
28197                         y -= sh - h;
28198                     break;
28199                 }
28200                 w = sw;
28201                 h = sh;
28202             }
28203
28204             if(this.preserveRatio){
28205                 switch(pos){
28206                     case "southeast":
28207                     case "east":
28208                         h = oh * (w/ow);
28209                         h = Math.min(Math.max(mh, h), mxh);
28210                         w = ow * (h/oh);
28211                        break;
28212                     case "south":
28213                         w = ow * (h/oh);
28214                         w = Math.min(Math.max(mw, w), mxw);
28215                         h = oh * (w/ow);
28216                         break;
28217                     case "northeast":
28218                         w = ow * (h/oh);
28219                         w = Math.min(Math.max(mw, w), mxw);
28220                         h = oh * (w/ow);
28221                     break;
28222                     case "north":
28223                         var tw = w;
28224                         w = ow * (h/oh);
28225                         w = Math.min(Math.max(mw, w), mxw);
28226                         h = oh * (w/ow);
28227                         x += (tw - w) / 2;
28228                         break;
28229                     case "southwest":
28230                         h = oh * (w/ow);
28231                         h = Math.min(Math.max(mh, h), mxh);
28232                         var tw = w;
28233                         w = ow * (h/oh);
28234                         x += tw - w;
28235                         break;
28236                     case "west":
28237                         var th = h;
28238                         h = oh * (w/ow);
28239                         h = Math.min(Math.max(mh, h), mxh);
28240                         y += (th - h) / 2;
28241                         var tw = w;
28242                         w = ow * (h/oh);
28243                         x += tw - w;
28244                        break;
28245                     case "northwest":
28246                         var tw = w;
28247                         var th = h;
28248                         h = oh * (w/ow);
28249                         h = Math.min(Math.max(mh, h), mxh);
28250                         w = ow * (h/oh);
28251                         y += th - h;
28252                         x += tw - w;
28253                        break;
28254
28255                 }
28256             }
28257             if (pos == 'hdrag') {
28258                 w = ow;
28259             }
28260             this.proxy.setBounds(x, y, w, h);
28261             if(this.dynamic){
28262                 this.resizeElement();
28263             }
28264             }catch(e){}
28265         }
28266     },
28267
28268     // private
28269     handleOver : function(){
28270         if(this.enabled){
28271             this.el.addClass("x-resizable-over");
28272         }
28273     },
28274
28275     // private
28276     handleOut : function(){
28277         if(!this.resizing){
28278             this.el.removeClass("x-resizable-over");
28279         }
28280     },
28281
28282     /**
28283      * Returns the element this component is bound to.
28284      * @return {Roo.Element}
28285      */
28286     getEl : function(){
28287         return this.el;
28288     },
28289
28290     /**
28291      * Returns the resizeChild element (or null).
28292      * @return {Roo.Element}
28293      */
28294     getResizeChild : function(){
28295         return this.resizeChild;
28296     },
28297
28298     /**
28299      * Destroys this resizable. If the element was wrapped and
28300      * removeEl is not true then the element remains.
28301      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
28302      */
28303     destroy : function(removeEl){
28304         this.proxy.remove();
28305         if(this.overlay){
28306             this.overlay.removeAllListeners();
28307             this.overlay.remove();
28308         }
28309         var ps = Roo.Resizable.positions;
28310         for(var k in ps){
28311             if(typeof ps[k] != "function" && this[ps[k]]){
28312                 var h = this[ps[k]];
28313                 h.el.removeAllListeners();
28314                 h.el.remove();
28315             }
28316         }
28317         if(removeEl){
28318             this.el.update("");
28319             this.el.remove();
28320         }
28321     }
28322 });
28323
28324 // private
28325 // hash to map config positions to true positions
28326 Roo.Resizable.positions = {
28327     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
28328     hd: "hdrag"
28329 };
28330
28331 // private
28332 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
28333     if(!this.tpl){
28334         // only initialize the template if resizable is used
28335         var tpl = Roo.DomHelper.createTemplate(
28336             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
28337         );
28338         tpl.compile();
28339         Roo.Resizable.Handle.prototype.tpl = tpl;
28340     }
28341     this.position = pos;
28342     this.rz = rz;
28343     // show north drag fro topdra
28344     var handlepos = pos == 'hdrag' ? 'north' : pos;
28345     
28346     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
28347     if (pos == 'hdrag') {
28348         this.el.setStyle('cursor', 'pointer');
28349     }
28350     this.el.unselectable();
28351     if(transparent){
28352         this.el.setOpacity(0);
28353     }
28354     this.el.on("mousedown", this.onMouseDown, this);
28355     if(!disableTrackOver){
28356         this.el.on("mouseover", this.onMouseOver, this);
28357         this.el.on("mouseout", this.onMouseOut, this);
28358     }
28359 };
28360
28361 // private
28362 Roo.Resizable.Handle.prototype = {
28363     afterResize : function(rz){
28364         // do nothing
28365     },
28366     // private
28367     onMouseDown : function(e){
28368         this.rz.onMouseDown(this, e);
28369     },
28370     // private
28371     onMouseOver : function(e){
28372         this.rz.handleOver(this, e);
28373     },
28374     // private
28375     onMouseOut : function(e){
28376         this.rz.handleOut(this, e);
28377     }
28378 };/*
28379  * Based on:
28380  * Ext JS Library 1.1.1
28381  * Copyright(c) 2006-2007, Ext JS, LLC.
28382  *
28383  * Originally Released Under LGPL - original licence link has changed is not relivant.
28384  *
28385  * Fork - LGPL
28386  * <script type="text/javascript">
28387  */
28388
28389 /**
28390  * @class Roo.Editor
28391  * @extends Roo.Component
28392  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
28393  * @constructor
28394  * Create a new Editor
28395  * @param {Roo.form.Field} field The Field object (or descendant)
28396  * @param {Object} config The config object
28397  */
28398 Roo.Editor = function(field, config){
28399     Roo.Editor.superclass.constructor.call(this, config);
28400     this.field = field;
28401     this.addEvents({
28402         /**
28403              * @event beforestartedit
28404              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
28405              * false from the handler of this event.
28406              * @param {Editor} this
28407              * @param {Roo.Element} boundEl The underlying element bound to this editor
28408              * @param {Mixed} value The field value being set
28409              */
28410         "beforestartedit" : true,
28411         /**
28412              * @event startedit
28413              * Fires when this editor is displayed
28414              * @param {Roo.Element} boundEl The underlying element bound to this editor
28415              * @param {Mixed} value The starting field value
28416              */
28417         "startedit" : true,
28418         /**
28419              * @event beforecomplete
28420              * Fires after a change has been made to the field, but before the change is reflected in the underlying
28421              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
28422              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
28423              * event will not fire since no edit actually occurred.
28424              * @param {Editor} this
28425              * @param {Mixed} value The current field value
28426              * @param {Mixed} startValue The original field value
28427              */
28428         "beforecomplete" : true,
28429         /**
28430              * @event complete
28431              * Fires after editing is complete and any changed value has been written to the underlying field.
28432              * @param {Editor} this
28433              * @param {Mixed} value The current field value
28434              * @param {Mixed} startValue The original field value
28435              */
28436         "complete" : true,
28437         /**
28438          * @event specialkey
28439          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
28440          * {@link Roo.EventObject#getKey} to determine which key was pressed.
28441          * @param {Roo.form.Field} this
28442          * @param {Roo.EventObject} e The event object
28443          */
28444         "specialkey" : true
28445     });
28446 };
28447
28448 Roo.extend(Roo.Editor, Roo.Component, {
28449     /**
28450      * @cfg {Boolean/String} autosize
28451      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
28452      * or "height" to adopt the height only (defaults to false)
28453      */
28454     /**
28455      * @cfg {Boolean} revertInvalid
28456      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
28457      * validation fails (defaults to true)
28458      */
28459     /**
28460      * @cfg {Boolean} ignoreNoChange
28461      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
28462      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
28463      * will never be ignored.
28464      */
28465     /**
28466      * @cfg {Boolean} hideEl
28467      * False to keep the bound element visible while the editor is displayed (defaults to true)
28468      */
28469     /**
28470      * @cfg {Mixed} value
28471      * The data value of the underlying field (defaults to "")
28472      */
28473     value : "",
28474     /**
28475      * @cfg {String} alignment
28476      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
28477      */
28478     alignment: "c-c?",
28479     /**
28480      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
28481      * for bottom-right shadow (defaults to "frame")
28482      */
28483     shadow : "frame",
28484     /**
28485      * @cfg {Boolean} constrain True to constrain the editor to the viewport
28486      */
28487     constrain : false,
28488     /**
28489      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
28490      */
28491     completeOnEnter : false,
28492     /**
28493      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
28494      */
28495     cancelOnEsc : false,
28496     /**
28497      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
28498      */
28499     updateEl : false,
28500
28501     // private
28502     onRender : function(ct, position){
28503         this.el = new Roo.Layer({
28504             shadow: this.shadow,
28505             cls: "x-editor",
28506             parentEl : ct,
28507             shim : this.shim,
28508             shadowOffset:4,
28509             id: this.id,
28510             constrain: this.constrain
28511         });
28512         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
28513         if(this.field.msgTarget != 'title'){
28514             this.field.msgTarget = 'qtip';
28515         }
28516         this.field.render(this.el);
28517         if(Roo.isGecko){
28518             this.field.el.dom.setAttribute('autocomplete', 'off');
28519         }
28520         this.field.on("specialkey", this.onSpecialKey, this);
28521         if(this.swallowKeys){
28522             this.field.el.swallowEvent(['keydown','keypress']);
28523         }
28524         this.field.show();
28525         this.field.on("blur", this.onBlur, this);
28526         if(this.field.grow){
28527             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
28528         }
28529     },
28530
28531     onSpecialKey : function(field, e)
28532     {
28533         //Roo.log('editor onSpecialKey');
28534         if(this.completeOnEnter && e.getKey() == e.ENTER){
28535             e.stopEvent();
28536             this.completeEdit();
28537             return;
28538         }
28539         // do not fire special key otherwise it might hide close the editor...
28540         if(e.getKey() == e.ENTER){    
28541             return;
28542         }
28543         if(this.cancelOnEsc && e.getKey() == e.ESC){
28544             this.cancelEdit();
28545             return;
28546         } 
28547         this.fireEvent('specialkey', field, e);
28548     
28549     },
28550
28551     /**
28552      * Starts the editing process and shows the editor.
28553      * @param {String/HTMLElement/Element} el The element to edit
28554      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
28555       * to the innerHTML of el.
28556      */
28557     startEdit : function(el, value){
28558         if(this.editing){
28559             this.completeEdit();
28560         }
28561         this.boundEl = Roo.get(el);
28562         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
28563         if(!this.rendered){
28564             this.render(this.parentEl || document.body);
28565         }
28566         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
28567             return;
28568         }
28569         this.startValue = v;
28570         this.field.setValue(v);
28571         if(this.autoSize){
28572             var sz = this.boundEl.getSize();
28573             switch(this.autoSize){
28574                 case "width":
28575                 this.setSize(sz.width,  "");
28576                 break;
28577                 case "height":
28578                 this.setSize("",  sz.height);
28579                 break;
28580                 default:
28581                 this.setSize(sz.width,  sz.height);
28582             }
28583         }
28584         this.el.alignTo(this.boundEl, this.alignment);
28585         this.editing = true;
28586         if(Roo.QuickTips){
28587             Roo.QuickTips.disable();
28588         }
28589         this.show();
28590     },
28591
28592     /**
28593      * Sets the height and width of this editor.
28594      * @param {Number} width The new width
28595      * @param {Number} height The new height
28596      */
28597     setSize : function(w, h){
28598         this.field.setSize(w, h);
28599         if(this.el){
28600             this.el.sync();
28601         }
28602     },
28603
28604     /**
28605      * Realigns the editor to the bound field based on the current alignment config value.
28606      */
28607     realign : function(){
28608         this.el.alignTo(this.boundEl, this.alignment);
28609     },
28610
28611     /**
28612      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
28613      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
28614      */
28615     completeEdit : function(remainVisible){
28616         if(!this.editing){
28617             return;
28618         }
28619         var v = this.getValue();
28620         if(this.revertInvalid !== false && !this.field.isValid()){
28621             v = this.startValue;
28622             this.cancelEdit(true);
28623         }
28624         if(String(v) === String(this.startValue) && this.ignoreNoChange){
28625             this.editing = false;
28626             this.hide();
28627             return;
28628         }
28629         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
28630             this.editing = false;
28631             if(this.updateEl && this.boundEl){
28632                 this.boundEl.update(v);
28633             }
28634             if(remainVisible !== true){
28635                 this.hide();
28636             }
28637             this.fireEvent("complete", this, v, this.startValue);
28638         }
28639     },
28640
28641     // private
28642     onShow : function(){
28643         this.el.show();
28644         if(this.hideEl !== false){
28645             this.boundEl.hide();
28646         }
28647         this.field.show();
28648         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
28649             this.fixIEFocus = true;
28650             this.deferredFocus.defer(50, this);
28651         }else{
28652             this.field.focus();
28653         }
28654         this.fireEvent("startedit", this.boundEl, this.startValue);
28655     },
28656
28657     deferredFocus : function(){
28658         if(this.editing){
28659             this.field.focus();
28660         }
28661     },
28662
28663     /**
28664      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
28665      * reverted to the original starting value.
28666      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
28667      * cancel (defaults to false)
28668      */
28669     cancelEdit : function(remainVisible){
28670         if(this.editing){
28671             this.setValue(this.startValue);
28672             if(remainVisible !== true){
28673                 this.hide();
28674             }
28675         }
28676     },
28677
28678     // private
28679     onBlur : function(){
28680         if(this.allowBlur !== true && this.editing){
28681             this.completeEdit();
28682         }
28683     },
28684
28685     // private
28686     onHide : function(){
28687         if(this.editing){
28688             this.completeEdit();
28689             return;
28690         }
28691         this.field.blur();
28692         if(this.field.collapse){
28693             this.field.collapse();
28694         }
28695         this.el.hide();
28696         if(this.hideEl !== false){
28697             this.boundEl.show();
28698         }
28699         if(Roo.QuickTips){
28700             Roo.QuickTips.enable();
28701         }
28702     },
28703
28704     /**
28705      * Sets the data value of the editor
28706      * @param {Mixed} value Any valid value supported by the underlying field
28707      */
28708     setValue : function(v){
28709         this.field.setValue(v);
28710     },
28711
28712     /**
28713      * Gets the data value of the editor
28714      * @return {Mixed} The data value
28715      */
28716     getValue : function(){
28717         return this.field.getValue();
28718     }
28719 });/*
28720  * Based on:
28721  * Ext JS Library 1.1.1
28722  * Copyright(c) 2006-2007, Ext JS, LLC.
28723  *
28724  * Originally Released Under LGPL - original licence link has changed is not relivant.
28725  *
28726  * Fork - LGPL
28727  * <script type="text/javascript">
28728  */
28729  
28730 /**
28731  * @class Roo.BasicDialog
28732  * @extends Roo.util.Observable
28733  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
28734  * <pre><code>
28735 var dlg = new Roo.BasicDialog("my-dlg", {
28736     height: 200,
28737     width: 300,
28738     minHeight: 100,
28739     minWidth: 150,
28740     modal: true,
28741     proxyDrag: true,
28742     shadow: true
28743 });
28744 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
28745 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
28746 dlg.addButton('Cancel', dlg.hide, dlg);
28747 dlg.show();
28748 </code></pre>
28749   <b>A Dialog should always be a direct child of the body element.</b>
28750  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
28751  * @cfg {String} title Default text to display in the title bar (defaults to null)
28752  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
28753  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
28754  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
28755  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
28756  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
28757  * (defaults to null with no animation)
28758  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
28759  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
28760  * property for valid values (defaults to 'all')
28761  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
28762  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
28763  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
28764  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
28765  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
28766  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
28767  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
28768  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
28769  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
28770  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
28771  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
28772  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
28773  * draggable = true (defaults to false)
28774  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
28775  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
28776  * shadow (defaults to false)
28777  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
28778  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
28779  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
28780  * @cfg {Array} buttons Array of buttons
28781  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
28782  * @constructor
28783  * Create a new BasicDialog.
28784  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
28785  * @param {Object} config Configuration options
28786  */
28787 Roo.BasicDialog = function(el, config){
28788     this.el = Roo.get(el);
28789     var dh = Roo.DomHelper;
28790     if(!this.el && config && config.autoCreate){
28791         if(typeof config.autoCreate == "object"){
28792             if(!config.autoCreate.id){
28793                 config.autoCreate.id = el;
28794             }
28795             this.el = dh.append(document.body,
28796                         config.autoCreate, true);
28797         }else{
28798             this.el = dh.append(document.body,
28799                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
28800         }
28801     }
28802     el = this.el;
28803     el.setDisplayed(true);
28804     el.hide = this.hideAction;
28805     this.id = el.id;
28806     el.addClass("x-dlg");
28807
28808     Roo.apply(this, config);
28809
28810     this.proxy = el.createProxy("x-dlg-proxy");
28811     this.proxy.hide = this.hideAction;
28812     this.proxy.setOpacity(.5);
28813     this.proxy.hide();
28814
28815     if(config.width){
28816         el.setWidth(config.width);
28817     }
28818     if(config.height){
28819         el.setHeight(config.height);
28820     }
28821     this.size = el.getSize();
28822     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
28823         this.xy = [config.x,config.y];
28824     }else{
28825         this.xy = el.getCenterXY(true);
28826     }
28827     /** The header element @type Roo.Element */
28828     this.header = el.child("> .x-dlg-hd");
28829     /** The body element @type Roo.Element */
28830     this.body = el.child("> .x-dlg-bd");
28831     /** The footer element @type Roo.Element */
28832     this.footer = el.child("> .x-dlg-ft");
28833
28834     if(!this.header){
28835         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
28836     }
28837     if(!this.body){
28838         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
28839     }
28840
28841     this.header.unselectable();
28842     if(this.title){
28843         this.header.update(this.title);
28844     }
28845     // this element allows the dialog to be focused for keyboard event
28846     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
28847     this.focusEl.swallowEvent("click", true);
28848
28849     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
28850
28851     // wrap the body and footer for special rendering
28852     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
28853     if(this.footer){
28854         this.bwrap.dom.appendChild(this.footer.dom);
28855     }
28856
28857     this.bg = this.el.createChild({
28858         tag: "div", cls:"x-dlg-bg",
28859         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
28860     });
28861     this.centerBg = this.bg.child("div.x-dlg-bg-center");
28862
28863
28864     if(this.autoScroll !== false && !this.autoTabs){
28865         this.body.setStyle("overflow", "auto");
28866     }
28867
28868     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
28869
28870     if(this.closable !== false){
28871         this.el.addClass("x-dlg-closable");
28872         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
28873         this.close.on("click", this.closeClick, this);
28874         this.close.addClassOnOver("x-dlg-close-over");
28875     }
28876     if(this.collapsible !== false){
28877         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
28878         this.collapseBtn.on("click", this.collapseClick, this);
28879         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
28880         this.header.on("dblclick", this.collapseClick, this);
28881     }
28882     if(this.resizable !== false){
28883         this.el.addClass("x-dlg-resizable");
28884         this.resizer = new Roo.Resizable(el, {
28885             minWidth: this.minWidth || 80,
28886             minHeight:this.minHeight || 80,
28887             handles: this.resizeHandles || "all",
28888             pinned: true
28889         });
28890         this.resizer.on("beforeresize", this.beforeResize, this);
28891         this.resizer.on("resize", this.onResize, this);
28892     }
28893     if(this.draggable !== false){
28894         el.addClass("x-dlg-draggable");
28895         if (!this.proxyDrag) {
28896             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
28897         }
28898         else {
28899             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
28900         }
28901         dd.setHandleElId(this.header.id);
28902         dd.endDrag = this.endMove.createDelegate(this);
28903         dd.startDrag = this.startMove.createDelegate(this);
28904         dd.onDrag = this.onDrag.createDelegate(this);
28905         dd.scroll = false;
28906         this.dd = dd;
28907     }
28908     if(this.modal){
28909         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
28910         this.mask.enableDisplayMode("block");
28911         this.mask.hide();
28912         this.el.addClass("x-dlg-modal");
28913     }
28914     if(this.shadow){
28915         this.shadow = new Roo.Shadow({
28916             mode : typeof this.shadow == "string" ? this.shadow : "sides",
28917             offset : this.shadowOffset
28918         });
28919     }else{
28920         this.shadowOffset = 0;
28921     }
28922     if(Roo.useShims && this.shim !== false){
28923         this.shim = this.el.createShim();
28924         this.shim.hide = this.hideAction;
28925         this.shim.hide();
28926     }else{
28927         this.shim = false;
28928     }
28929     if(this.autoTabs){
28930         this.initTabs();
28931     }
28932     if (this.buttons) { 
28933         var bts= this.buttons;
28934         this.buttons = [];
28935         Roo.each(bts, function(b) {
28936             this.addButton(b);
28937         }, this);
28938     }
28939     
28940     
28941     this.addEvents({
28942         /**
28943          * @event keydown
28944          * Fires when a key is pressed
28945          * @param {Roo.BasicDialog} this
28946          * @param {Roo.EventObject} e
28947          */
28948         "keydown" : true,
28949         /**
28950          * @event move
28951          * Fires when this dialog is moved by the user.
28952          * @param {Roo.BasicDialog} this
28953          * @param {Number} x The new page X
28954          * @param {Number} y The new page Y
28955          */
28956         "move" : true,
28957         /**
28958          * @event resize
28959          * Fires when this dialog is resized by the user.
28960          * @param {Roo.BasicDialog} this
28961          * @param {Number} width The new width
28962          * @param {Number} height The new height
28963          */
28964         "resize" : true,
28965         /**
28966          * @event beforehide
28967          * Fires before this dialog is hidden.
28968          * @param {Roo.BasicDialog} this
28969          */
28970         "beforehide" : true,
28971         /**
28972          * @event hide
28973          * Fires when this dialog is hidden.
28974          * @param {Roo.BasicDialog} this
28975          */
28976         "hide" : true,
28977         /**
28978          * @event beforeshow
28979          * Fires before this dialog is shown.
28980          * @param {Roo.BasicDialog} this
28981          */
28982         "beforeshow" : true,
28983         /**
28984          * @event show
28985          * Fires when this dialog is shown.
28986          * @param {Roo.BasicDialog} this
28987          */
28988         "show" : true
28989     });
28990     el.on("keydown", this.onKeyDown, this);
28991     el.on("mousedown", this.toFront, this);
28992     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
28993     this.el.hide();
28994     Roo.DialogManager.register(this);
28995     Roo.BasicDialog.superclass.constructor.call(this);
28996 };
28997
28998 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
28999     shadowOffset: Roo.isIE ? 6 : 5,
29000     minHeight: 80,
29001     minWidth: 200,
29002     minButtonWidth: 75,
29003     defaultButton: null,
29004     buttonAlign: "right",
29005     tabTag: 'div',
29006     firstShow: true,
29007
29008     /**
29009      * Sets the dialog title text
29010      * @param {String} text The title text to display
29011      * @return {Roo.BasicDialog} this
29012      */
29013     setTitle : function(text){
29014         this.header.update(text);
29015         return this;
29016     },
29017
29018     // private
29019     closeClick : function(){
29020         this.hide();
29021     },
29022
29023     // private
29024     collapseClick : function(){
29025         this[this.collapsed ? "expand" : "collapse"]();
29026     },
29027
29028     /**
29029      * Collapses the dialog to its minimized state (only the title bar is visible).
29030      * Equivalent to the user clicking the collapse dialog button.
29031      */
29032     collapse : function(){
29033         if(!this.collapsed){
29034             this.collapsed = true;
29035             this.el.addClass("x-dlg-collapsed");
29036             this.restoreHeight = this.el.getHeight();
29037             this.resizeTo(this.el.getWidth(), this.header.getHeight());
29038         }
29039     },
29040
29041     /**
29042      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
29043      * clicking the expand dialog button.
29044      */
29045     expand : function(){
29046         if(this.collapsed){
29047             this.collapsed = false;
29048             this.el.removeClass("x-dlg-collapsed");
29049             this.resizeTo(this.el.getWidth(), this.restoreHeight);
29050         }
29051     },
29052
29053     /**
29054      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
29055      * @return {Roo.TabPanel} The tabs component
29056      */
29057     initTabs : function(){
29058         var tabs = this.getTabs();
29059         while(tabs.getTab(0)){
29060             tabs.removeTab(0);
29061         }
29062         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
29063             var dom = el.dom;
29064             tabs.addTab(Roo.id(dom), dom.title);
29065             dom.title = "";
29066         });
29067         tabs.activate(0);
29068         return tabs;
29069     },
29070
29071     // private
29072     beforeResize : function(){
29073         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
29074     },
29075
29076     // private
29077     onResize : function(){
29078         this.refreshSize();
29079         this.syncBodyHeight();
29080         this.adjustAssets();
29081         this.focus();
29082         this.fireEvent("resize", this, this.size.width, this.size.height);
29083     },
29084
29085     // private
29086     onKeyDown : function(e){
29087         if(this.isVisible()){
29088             this.fireEvent("keydown", this, e);
29089         }
29090     },
29091
29092     /**
29093      * Resizes the dialog.
29094      * @param {Number} width
29095      * @param {Number} height
29096      * @return {Roo.BasicDialog} this
29097      */
29098     resizeTo : function(width, height){
29099         this.el.setSize(width, height);
29100         this.size = {width: width, height: height};
29101         this.syncBodyHeight();
29102         if(this.fixedcenter){
29103             this.center();
29104         }
29105         if(this.isVisible()){
29106             this.constrainXY();
29107             this.adjustAssets();
29108         }
29109         this.fireEvent("resize", this, width, height);
29110         return this;
29111     },
29112
29113
29114     /**
29115      * Resizes the dialog to fit the specified content size.
29116      * @param {Number} width
29117      * @param {Number} height
29118      * @return {Roo.BasicDialog} this
29119      */
29120     setContentSize : function(w, h){
29121         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
29122         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
29123         //if(!this.el.isBorderBox()){
29124             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
29125             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
29126         //}
29127         if(this.tabs){
29128             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
29129             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
29130         }
29131         this.resizeTo(w, h);
29132         return this;
29133     },
29134
29135     /**
29136      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
29137      * executed in response to a particular key being pressed while the dialog is active.
29138      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
29139      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
29140      * @param {Function} fn The function to call
29141      * @param {Object} scope (optional) The scope of the function
29142      * @return {Roo.BasicDialog} this
29143      */
29144     addKeyListener : function(key, fn, scope){
29145         var keyCode, shift, ctrl, alt;
29146         if(typeof key == "object" && !(key instanceof Array)){
29147             keyCode = key["key"];
29148             shift = key["shift"];
29149             ctrl = key["ctrl"];
29150             alt = key["alt"];
29151         }else{
29152             keyCode = key;
29153         }
29154         var handler = function(dlg, e){
29155             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
29156                 var k = e.getKey();
29157                 if(keyCode instanceof Array){
29158                     for(var i = 0, len = keyCode.length; i < len; i++){
29159                         if(keyCode[i] == k){
29160                           fn.call(scope || window, dlg, k, e);
29161                           return;
29162                         }
29163                     }
29164                 }else{
29165                     if(k == keyCode){
29166                         fn.call(scope || window, dlg, k, e);
29167                     }
29168                 }
29169             }
29170         };
29171         this.on("keydown", handler);
29172         return this;
29173     },
29174
29175     /**
29176      * Returns the TabPanel component (creates it if it doesn't exist).
29177      * Note: If you wish to simply check for the existence of tabs without creating them,
29178      * check for a null 'tabs' property.
29179      * @return {Roo.TabPanel} The tabs component
29180      */
29181     getTabs : function(){
29182         if(!this.tabs){
29183             this.el.addClass("x-dlg-auto-tabs");
29184             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
29185             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
29186         }
29187         return this.tabs;
29188     },
29189
29190     /**
29191      * Adds a button to the footer section of the dialog.
29192      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
29193      * object or a valid Roo.DomHelper element config
29194      * @param {Function} handler The function called when the button is clicked
29195      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
29196      * @return {Roo.Button} The new button
29197      */
29198     addButton : function(config, handler, scope){
29199         var dh = Roo.DomHelper;
29200         if(!this.footer){
29201             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
29202         }
29203         if(!this.btnContainer){
29204             var tb = this.footer.createChild({
29205
29206                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
29207                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
29208             }, null, true);
29209             this.btnContainer = tb.firstChild.firstChild.firstChild;
29210         }
29211         var bconfig = {
29212             handler: handler,
29213             scope: scope,
29214             minWidth: this.minButtonWidth,
29215             hideParent:true
29216         };
29217         if(typeof config == "string"){
29218             bconfig.text = config;
29219         }else{
29220             if(config.tag){
29221                 bconfig.dhconfig = config;
29222             }else{
29223                 Roo.apply(bconfig, config);
29224             }
29225         }
29226         var fc = false;
29227         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
29228             bconfig.position = Math.max(0, bconfig.position);
29229             fc = this.btnContainer.childNodes[bconfig.position];
29230         }
29231          
29232         var btn = new Roo.Button(
29233             fc ? 
29234                 this.btnContainer.insertBefore(document.createElement("td"),fc)
29235                 : this.btnContainer.appendChild(document.createElement("td")),
29236             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
29237             bconfig
29238         );
29239         this.syncBodyHeight();
29240         if(!this.buttons){
29241             /**
29242              * Array of all the buttons that have been added to this dialog via addButton
29243              * @type Array
29244              */
29245             this.buttons = [];
29246         }
29247         this.buttons.push(btn);
29248         return btn;
29249     },
29250
29251     /**
29252      * Sets the default button to be focused when the dialog is displayed.
29253      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
29254      * @return {Roo.BasicDialog} this
29255      */
29256     setDefaultButton : function(btn){
29257         this.defaultButton = btn;
29258         return this;
29259     },
29260
29261     // private
29262     getHeaderFooterHeight : function(safe){
29263         var height = 0;
29264         if(this.header){
29265            height += this.header.getHeight();
29266         }
29267         if(this.footer){
29268            var fm = this.footer.getMargins();
29269             height += (this.footer.getHeight()+fm.top+fm.bottom);
29270         }
29271         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
29272         height += this.centerBg.getPadding("tb");
29273         return height;
29274     },
29275
29276     // private
29277     syncBodyHeight : function(){
29278         var bd = this.body, cb = this.centerBg, bw = this.bwrap;
29279         var height = this.size.height - this.getHeaderFooterHeight(false);
29280         bd.setHeight(height-bd.getMargins("tb"));
29281         var hh = this.header.getHeight();
29282         var h = this.size.height-hh;
29283         cb.setHeight(h);
29284         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
29285         bw.setHeight(h-cb.getPadding("tb"));
29286         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
29287         bd.setWidth(bw.getWidth(true));
29288         if(this.tabs){
29289             this.tabs.syncHeight();
29290             if(Roo.isIE){
29291                 this.tabs.el.repaint();
29292             }
29293         }
29294     },
29295
29296     /**
29297      * Restores the previous state of the dialog if Roo.state is configured.
29298      * @return {Roo.BasicDialog} this
29299      */
29300     restoreState : function(){
29301         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
29302         if(box && box.width){
29303             this.xy = [box.x, box.y];
29304             this.resizeTo(box.width, box.height);
29305         }
29306         return this;
29307     },
29308
29309     // private
29310     beforeShow : function(){
29311         this.expand();
29312         if(this.fixedcenter){
29313             this.xy = this.el.getCenterXY(true);
29314         }
29315         if(this.modal){
29316             Roo.get(document.body).addClass("x-body-masked");
29317             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29318             this.mask.show();
29319         }
29320         this.constrainXY();
29321     },
29322
29323     // private
29324     animShow : function(){
29325         var b = Roo.get(this.animateTarget).getBox();
29326         this.proxy.setSize(b.width, b.height);
29327         this.proxy.setLocation(b.x, b.y);
29328         this.proxy.show();
29329         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
29330                     true, .35, this.showEl.createDelegate(this));
29331     },
29332
29333     /**
29334      * Shows the dialog.
29335      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
29336      * @return {Roo.BasicDialog} this
29337      */
29338     show : function(animateTarget){
29339         if (this.fireEvent("beforeshow", this) === false){
29340             return;
29341         }
29342         if(this.syncHeightBeforeShow){
29343             this.syncBodyHeight();
29344         }else if(this.firstShow){
29345             this.firstShow = false;
29346             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
29347         }
29348         this.animateTarget = animateTarget || this.animateTarget;
29349         if(!this.el.isVisible()){
29350             this.beforeShow();
29351             if(this.animateTarget && Roo.get(this.animateTarget)){
29352                 this.animShow();
29353             }else{
29354                 this.showEl();
29355             }
29356         }
29357         return this;
29358     },
29359
29360     // private
29361     showEl : function(){
29362         this.proxy.hide();
29363         this.el.setXY(this.xy);
29364         this.el.show();
29365         this.adjustAssets(true);
29366         this.toFront();
29367         this.focus();
29368         // IE peekaboo bug - fix found by Dave Fenwick
29369         if(Roo.isIE){
29370             this.el.repaint();
29371         }
29372         this.fireEvent("show", this);
29373     },
29374
29375     /**
29376      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
29377      * dialog itself will receive focus.
29378      */
29379     focus : function(){
29380         if(this.defaultButton){
29381             this.defaultButton.focus();
29382         }else{
29383             this.focusEl.focus();
29384         }
29385     },
29386
29387     // private
29388     constrainXY : function(){
29389         if(this.constraintoviewport !== false){
29390             if(!this.viewSize){
29391                 if(this.container){
29392                     var s = this.container.getSize();
29393                     this.viewSize = [s.width, s.height];
29394                 }else{
29395                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
29396                 }
29397             }
29398             var s = Roo.get(this.container||document).getScroll();
29399
29400             var x = this.xy[0], y = this.xy[1];
29401             var w = this.size.width, h = this.size.height;
29402             var vw = this.viewSize[0], vh = this.viewSize[1];
29403             // only move it if it needs it
29404             var moved = false;
29405             // first validate right/bottom
29406             if(x + w > vw+s.left){
29407                 x = vw - w;
29408                 moved = true;
29409             }
29410             if(y + h > vh+s.top){
29411                 y = vh - h;
29412                 moved = true;
29413             }
29414             // then make sure top/left isn't negative
29415             if(x < s.left){
29416                 x = s.left;
29417                 moved = true;
29418             }
29419             if(y < s.top){
29420                 y = s.top;
29421                 moved = true;
29422             }
29423             if(moved){
29424                 // cache xy
29425                 this.xy = [x, y];
29426                 if(this.isVisible()){
29427                     this.el.setLocation(x, y);
29428                     this.adjustAssets();
29429                 }
29430             }
29431         }
29432     },
29433
29434     // private
29435     onDrag : function(){
29436         if(!this.proxyDrag){
29437             this.xy = this.el.getXY();
29438             this.adjustAssets();
29439         }
29440     },
29441
29442     // private
29443     adjustAssets : function(doShow){
29444         var x = this.xy[0], y = this.xy[1];
29445         var w = this.size.width, h = this.size.height;
29446         if(doShow === true){
29447             if(this.shadow){
29448                 this.shadow.show(this.el);
29449             }
29450             if(this.shim){
29451                 this.shim.show();
29452             }
29453         }
29454         if(this.shadow && this.shadow.isVisible()){
29455             this.shadow.show(this.el);
29456         }
29457         if(this.shim && this.shim.isVisible()){
29458             this.shim.setBounds(x, y, w, h);
29459         }
29460     },
29461
29462     // private
29463     adjustViewport : function(w, h){
29464         if(!w || !h){
29465             w = Roo.lib.Dom.getViewWidth();
29466             h = Roo.lib.Dom.getViewHeight();
29467         }
29468         // cache the size
29469         this.viewSize = [w, h];
29470         if(this.modal && this.mask.isVisible()){
29471             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
29472             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29473         }
29474         if(this.isVisible()){
29475             this.constrainXY();
29476         }
29477     },
29478
29479     /**
29480      * Destroys this dialog and all its supporting elements (including any tabs, shim,
29481      * shadow, proxy, mask, etc.)  Also removes all event listeners.
29482      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
29483      */
29484     destroy : function(removeEl){
29485         if(this.isVisible()){
29486             this.animateTarget = null;
29487             this.hide();
29488         }
29489         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
29490         if(this.tabs){
29491             this.tabs.destroy(removeEl);
29492         }
29493         Roo.destroy(
29494              this.shim,
29495              this.proxy,
29496              this.resizer,
29497              this.close,
29498              this.mask
29499         );
29500         if(this.dd){
29501             this.dd.unreg();
29502         }
29503         if(this.buttons){
29504            for(var i = 0, len = this.buttons.length; i < len; i++){
29505                this.buttons[i].destroy();
29506            }
29507         }
29508         this.el.removeAllListeners();
29509         if(removeEl === true){
29510             this.el.update("");
29511             this.el.remove();
29512         }
29513         Roo.DialogManager.unregister(this);
29514     },
29515
29516     // private
29517     startMove : function(){
29518         if(this.proxyDrag){
29519             this.proxy.show();
29520         }
29521         if(this.constraintoviewport !== false){
29522             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
29523         }
29524     },
29525
29526     // private
29527     endMove : function(){
29528         if(!this.proxyDrag){
29529             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
29530         }else{
29531             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
29532             this.proxy.hide();
29533         }
29534         this.refreshSize();
29535         this.adjustAssets();
29536         this.focus();
29537         this.fireEvent("move", this, this.xy[0], this.xy[1]);
29538     },
29539
29540     /**
29541      * Brings this dialog to the front of any other visible dialogs
29542      * @return {Roo.BasicDialog} this
29543      */
29544     toFront : function(){
29545         Roo.DialogManager.bringToFront(this);
29546         return this;
29547     },
29548
29549     /**
29550      * Sends this dialog to the back (under) of any other visible dialogs
29551      * @return {Roo.BasicDialog} this
29552      */
29553     toBack : function(){
29554         Roo.DialogManager.sendToBack(this);
29555         return this;
29556     },
29557
29558     /**
29559      * Centers this dialog in the viewport
29560      * @return {Roo.BasicDialog} this
29561      */
29562     center : function(){
29563         var xy = this.el.getCenterXY(true);
29564         this.moveTo(xy[0], xy[1]);
29565         return this;
29566     },
29567
29568     /**
29569      * Moves the dialog's top-left corner to the specified point
29570      * @param {Number} x
29571      * @param {Number} y
29572      * @return {Roo.BasicDialog} this
29573      */
29574     moveTo : function(x, y){
29575         this.xy = [x,y];
29576         if(this.isVisible()){
29577             this.el.setXY(this.xy);
29578             this.adjustAssets();
29579         }
29580         return this;
29581     },
29582
29583     /**
29584      * Aligns the dialog to the specified element
29585      * @param {String/HTMLElement/Roo.Element} element The element to align to.
29586      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
29587      * @param {Array} offsets (optional) Offset the positioning by [x, y]
29588      * @return {Roo.BasicDialog} this
29589      */
29590     alignTo : function(element, position, offsets){
29591         this.xy = this.el.getAlignToXY(element, position, offsets);
29592         if(this.isVisible()){
29593             this.el.setXY(this.xy);
29594             this.adjustAssets();
29595         }
29596         return this;
29597     },
29598
29599     /**
29600      * Anchors an element to another element and realigns it when the window is resized.
29601      * @param {String/HTMLElement/Roo.Element} element The element to align to.
29602      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
29603      * @param {Array} offsets (optional) Offset the positioning by [x, y]
29604      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
29605      * is a number, it is used as the buffer delay (defaults to 50ms).
29606      * @return {Roo.BasicDialog} this
29607      */
29608     anchorTo : function(el, alignment, offsets, monitorScroll){
29609         var action = function(){
29610             this.alignTo(el, alignment, offsets);
29611         };
29612         Roo.EventManager.onWindowResize(action, this);
29613         var tm = typeof monitorScroll;
29614         if(tm != 'undefined'){
29615             Roo.EventManager.on(window, 'scroll', action, this,
29616                 {buffer: tm == 'number' ? monitorScroll : 50});
29617         }
29618         action.call(this);
29619         return this;
29620     },
29621
29622     /**
29623      * Returns true if the dialog is visible
29624      * @return {Boolean}
29625      */
29626     isVisible : function(){
29627         return this.el.isVisible();
29628     },
29629
29630     // private
29631     animHide : function(callback){
29632         var b = Roo.get(this.animateTarget).getBox();
29633         this.proxy.show();
29634         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
29635         this.el.hide();
29636         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
29637                     this.hideEl.createDelegate(this, [callback]));
29638     },
29639
29640     /**
29641      * Hides the dialog.
29642      * @param {Function} callback (optional) Function to call when the dialog is hidden
29643      * @return {Roo.BasicDialog} this
29644      */
29645     hide : function(callback){
29646         if (this.fireEvent("beforehide", this) === false){
29647             return;
29648         }
29649         if(this.shadow){
29650             this.shadow.hide();
29651         }
29652         if(this.shim) {
29653           this.shim.hide();
29654         }
29655         // sometimes animateTarget seems to get set.. causing problems...
29656         // this just double checks..
29657         if(this.animateTarget && Roo.get(this.animateTarget)) {
29658            this.animHide(callback);
29659         }else{
29660             this.el.hide();
29661             this.hideEl(callback);
29662         }
29663         return this;
29664     },
29665
29666     // private
29667     hideEl : function(callback){
29668         this.proxy.hide();
29669         if(this.modal){
29670             this.mask.hide();
29671             Roo.get(document.body).removeClass("x-body-masked");
29672         }
29673         this.fireEvent("hide", this);
29674         if(typeof callback == "function"){
29675             callback();
29676         }
29677     },
29678
29679     // private
29680     hideAction : function(){
29681         this.setLeft("-10000px");
29682         this.setTop("-10000px");
29683         this.setStyle("visibility", "hidden");
29684     },
29685
29686     // private
29687     refreshSize : function(){
29688         this.size = this.el.getSize();
29689         this.xy = this.el.getXY();
29690         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
29691     },
29692
29693     // private
29694     // z-index is managed by the DialogManager and may be overwritten at any time
29695     setZIndex : function(index){
29696         if(this.modal){
29697             this.mask.setStyle("z-index", index);
29698         }
29699         if(this.shim){
29700             this.shim.setStyle("z-index", ++index);
29701         }
29702         if(this.shadow){
29703             this.shadow.setZIndex(++index);
29704         }
29705         this.el.setStyle("z-index", ++index);
29706         if(this.proxy){
29707             this.proxy.setStyle("z-index", ++index);
29708         }
29709         if(this.resizer){
29710             this.resizer.proxy.setStyle("z-index", ++index);
29711         }
29712
29713         this.lastZIndex = index;
29714     },
29715
29716     /**
29717      * Returns the element for this dialog
29718      * @return {Roo.Element} The underlying dialog Element
29719      */
29720     getEl : function(){
29721         return this.el;
29722     }
29723 });
29724
29725 /**
29726  * @class Roo.DialogManager
29727  * Provides global access to BasicDialogs that have been created and
29728  * support for z-indexing (layering) multiple open dialogs.
29729  */
29730 Roo.DialogManager = function(){
29731     var list = {};
29732     var accessList = [];
29733     var front = null;
29734
29735     // private
29736     var sortDialogs = function(d1, d2){
29737         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
29738     };
29739
29740     // private
29741     var orderDialogs = function(){
29742         accessList.sort(sortDialogs);
29743         var seed = Roo.DialogManager.zseed;
29744         for(var i = 0, len = accessList.length; i < len; i++){
29745             var dlg = accessList[i];
29746             if(dlg){
29747                 dlg.setZIndex(seed + (i*10));
29748             }
29749         }
29750     };
29751
29752     return {
29753         /**
29754          * The starting z-index for BasicDialogs (defaults to 9000)
29755          * @type Number The z-index value
29756          */
29757         zseed : 9000,
29758
29759         // private
29760         register : function(dlg){
29761             list[dlg.id] = dlg;
29762             accessList.push(dlg);
29763         },
29764
29765         // private
29766         unregister : function(dlg){
29767             delete list[dlg.id];
29768             var i=0;
29769             var len=0;
29770             if(!accessList.indexOf){
29771                 for(  i = 0, len = accessList.length; i < len; i++){
29772                     if(accessList[i] == dlg){
29773                         accessList.splice(i, 1);
29774                         return;
29775                     }
29776                 }
29777             }else{
29778                  i = accessList.indexOf(dlg);
29779                 if(i != -1){
29780                     accessList.splice(i, 1);
29781                 }
29782             }
29783         },
29784
29785         /**
29786          * Gets a registered dialog by id
29787          * @param {String/Object} id The id of the dialog or a dialog
29788          * @return {Roo.BasicDialog} this
29789          */
29790         get : function(id){
29791             return typeof id == "object" ? id : list[id];
29792         },
29793
29794         /**
29795          * Brings the specified dialog to the front
29796          * @param {String/Object} dlg The id of the dialog or a dialog
29797          * @return {Roo.BasicDialog} this
29798          */
29799         bringToFront : function(dlg){
29800             dlg = this.get(dlg);
29801             if(dlg != front){
29802                 front = dlg;
29803                 dlg._lastAccess = new Date().getTime();
29804                 orderDialogs();
29805             }
29806             return dlg;
29807         },
29808
29809         /**
29810          * Sends the specified dialog to the back
29811          * @param {String/Object} dlg The id of the dialog or a dialog
29812          * @return {Roo.BasicDialog} this
29813          */
29814         sendToBack : function(dlg){
29815             dlg = this.get(dlg);
29816             dlg._lastAccess = -(new Date().getTime());
29817             orderDialogs();
29818             return dlg;
29819         },
29820
29821         /**
29822          * Hides all dialogs
29823          */
29824         hideAll : function(){
29825             for(var id in list){
29826                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
29827                     list[id].hide();
29828                 }
29829             }
29830         }
29831     };
29832 }();
29833
29834 /**
29835  * @class Roo.LayoutDialog
29836  * @extends Roo.BasicDialog
29837  * Dialog which provides adjustments for working with a layout in a Dialog.
29838  * Add your necessary layout config options to the dialog's config.<br>
29839  * Example usage (including a nested layout):
29840  * <pre><code>
29841 if(!dialog){
29842     dialog = new Roo.LayoutDialog("download-dlg", {
29843         modal: true,
29844         width:600,
29845         height:450,
29846         shadow:true,
29847         minWidth:500,
29848         minHeight:350,
29849         autoTabs:true,
29850         proxyDrag:true,
29851         // layout config merges with the dialog config
29852         center:{
29853             tabPosition: "top",
29854             alwaysShowTabs: true
29855         }
29856     });
29857     dialog.addKeyListener(27, dialog.hide, dialog);
29858     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
29859     dialog.addButton("Build It!", this.getDownload, this);
29860
29861     // we can even add nested layouts
29862     var innerLayout = new Roo.BorderLayout("dl-inner", {
29863         east: {
29864             initialSize: 200,
29865             autoScroll:true,
29866             split:true
29867         },
29868         center: {
29869             autoScroll:true
29870         }
29871     });
29872     innerLayout.beginUpdate();
29873     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
29874     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
29875     innerLayout.endUpdate(true);
29876
29877     var layout = dialog.getLayout();
29878     layout.beginUpdate();
29879     layout.add("center", new Roo.ContentPanel("standard-panel",
29880                         {title: "Download the Source", fitToFrame:true}));
29881     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
29882                {title: "Build your own roo.js"}));
29883     layout.getRegion("center").showPanel(sp);
29884     layout.endUpdate();
29885 }
29886 </code></pre>
29887     * @constructor
29888     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
29889     * @param {Object} config configuration options
29890   */
29891 Roo.LayoutDialog = function(el, cfg){
29892     
29893     var config=  cfg;
29894     if (typeof(cfg) == 'undefined') {
29895         config = Roo.apply({}, el);
29896         // not sure why we use documentElement here.. - it should always be body.
29897         // IE7 borks horribly if we use documentElement.
29898         // webkit also does not like documentElement - it creates a body element...
29899         el = Roo.get( document.body || document.documentElement ).createChild();
29900         //config.autoCreate = true;
29901     }
29902     
29903     
29904     config.autoTabs = false;
29905     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
29906     this.body.setStyle({overflow:"hidden", position:"relative"});
29907     this.layout = new Roo.BorderLayout(this.body.dom, config);
29908     this.layout.monitorWindowResize = false;
29909     this.el.addClass("x-dlg-auto-layout");
29910     // fix case when center region overwrites center function
29911     this.center = Roo.BasicDialog.prototype.center;
29912     this.on("show", this.layout.layout, this.layout, true);
29913     if (config.items) {
29914         var xitems = config.items;
29915         delete config.items;
29916         Roo.each(xitems, this.addxtype, this);
29917     }
29918     
29919     
29920 };
29921 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
29922     /**
29923      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
29924      * @deprecated
29925      */
29926     endUpdate : function(){
29927         this.layout.endUpdate();
29928     },
29929
29930     /**
29931      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
29932      *  @deprecated
29933      */
29934     beginUpdate : function(){
29935         this.layout.beginUpdate();
29936     },
29937
29938     /**
29939      * Get the BorderLayout for this dialog
29940      * @return {Roo.BorderLayout}
29941      */
29942     getLayout : function(){
29943         return this.layout;
29944     },
29945
29946     showEl : function(){
29947         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
29948         if(Roo.isIE7){
29949             this.layout.layout();
29950         }
29951     },
29952
29953     // private
29954     // Use the syncHeightBeforeShow config option to control this automatically
29955     syncBodyHeight : function(){
29956         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
29957         if(this.layout){this.layout.layout();}
29958     },
29959     
29960       /**
29961      * Add an xtype element (actually adds to the layout.)
29962      * @return {Object} xdata xtype object data.
29963      */
29964     
29965     addxtype : function(c) {
29966         return this.layout.addxtype(c);
29967     }
29968 });/*
29969  * Based on:
29970  * Ext JS Library 1.1.1
29971  * Copyright(c) 2006-2007, Ext JS, LLC.
29972  *
29973  * Originally Released Under LGPL - original licence link has changed is not relivant.
29974  *
29975  * Fork - LGPL
29976  * <script type="text/javascript">
29977  */
29978  
29979 /**
29980  * @class Roo.MessageBox
29981  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
29982  * Example usage:
29983  *<pre><code>
29984 // Basic alert:
29985 Roo.Msg.alert('Status', 'Changes saved successfully.');
29986
29987 // Prompt for user data:
29988 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
29989     if (btn == 'ok'){
29990         // process text value...
29991     }
29992 });
29993
29994 // Show a dialog using config options:
29995 Roo.Msg.show({
29996    title:'Save Changes?',
29997    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
29998    buttons: Roo.Msg.YESNOCANCEL,
29999    fn: processResult,
30000    animEl: 'elId'
30001 });
30002 </code></pre>
30003  * @singleton
30004  */
30005 Roo.MessageBox = function(){
30006     var dlg, opt, mask, waitTimer;
30007     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
30008     var buttons, activeTextEl, bwidth;
30009
30010     // private
30011     var handleButton = function(button){
30012         dlg.hide();
30013         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
30014     };
30015
30016     // private
30017     var handleHide = function(){
30018         if(opt && opt.cls){
30019             dlg.el.removeClass(opt.cls);
30020         }
30021         if(waitTimer){
30022             Roo.TaskMgr.stop(waitTimer);
30023             waitTimer = null;
30024         }
30025     };
30026
30027     // private
30028     var updateButtons = function(b){
30029         var width = 0;
30030         if(!b){
30031             buttons["ok"].hide();
30032             buttons["cancel"].hide();
30033             buttons["yes"].hide();
30034             buttons["no"].hide();
30035             dlg.footer.dom.style.display = 'none';
30036             return width;
30037         }
30038         dlg.footer.dom.style.display = '';
30039         for(var k in buttons){
30040             if(typeof buttons[k] != "function"){
30041                 if(b[k]){
30042                     buttons[k].show();
30043                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
30044                     width += buttons[k].el.getWidth()+15;
30045                 }else{
30046                     buttons[k].hide();
30047                 }
30048             }
30049         }
30050         return width;
30051     };
30052
30053     // private
30054     var handleEsc = function(d, k, e){
30055         if(opt && opt.closable !== false){
30056             dlg.hide();
30057         }
30058         if(e){
30059             e.stopEvent();
30060         }
30061     };
30062
30063     return {
30064         /**
30065          * Returns a reference to the underlying {@link Roo.BasicDialog} element
30066          * @return {Roo.BasicDialog} The BasicDialog element
30067          */
30068         getDialog : function(){
30069            if(!dlg){
30070                 dlg = new Roo.BasicDialog("x-msg-box", {
30071                     autoCreate : true,
30072                     shadow: true,
30073                     draggable: true,
30074                     resizable:false,
30075                     constraintoviewport:false,
30076                     fixedcenter:true,
30077                     collapsible : false,
30078                     shim:true,
30079                     modal: true,
30080                     width:400, height:100,
30081                     buttonAlign:"center",
30082                     closeClick : function(){
30083                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
30084                             handleButton("no");
30085                         }else{
30086                             handleButton("cancel");
30087                         }
30088                     }
30089                 });
30090                 dlg.on("hide", handleHide);
30091                 mask = dlg.mask;
30092                 dlg.addKeyListener(27, handleEsc);
30093                 buttons = {};
30094                 var bt = this.buttonText;
30095                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
30096                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
30097                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
30098                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
30099                 bodyEl = dlg.body.createChild({
30100
30101                     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>'
30102                 });
30103                 msgEl = bodyEl.dom.firstChild;
30104                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
30105                 textboxEl.enableDisplayMode();
30106                 textboxEl.addKeyListener([10,13], function(){
30107                     if(dlg.isVisible() && opt && opt.buttons){
30108                         if(opt.buttons.ok){
30109                             handleButton("ok");
30110                         }else if(opt.buttons.yes){
30111                             handleButton("yes");
30112                         }
30113                     }
30114                 });
30115                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
30116                 textareaEl.enableDisplayMode();
30117                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
30118                 progressEl.enableDisplayMode();
30119                 var pf = progressEl.dom.firstChild;
30120                 if (pf) {
30121                     pp = Roo.get(pf.firstChild);
30122                     pp.setHeight(pf.offsetHeight);
30123                 }
30124                 
30125             }
30126             return dlg;
30127         },
30128
30129         /**
30130          * Updates the message box body text
30131          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
30132          * the XHTML-compliant non-breaking space character '&amp;#160;')
30133          * @return {Roo.MessageBox} This message box
30134          */
30135         updateText : function(text){
30136             if(!dlg.isVisible() && !opt.width){
30137                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
30138             }
30139             msgEl.innerHTML = text || '&#160;';
30140             var w = Math.max(Math.min(opt.width || msgEl.offsetWidth, this.maxWidth), 
30141                         Math.max(opt.minWidth || this.minWidth, bwidth));
30142             if(opt.prompt){
30143                 activeTextEl.setWidth(w);
30144             }
30145             if(dlg.isVisible()){
30146                 dlg.fixedcenter = false;
30147             }
30148             // to big, make it scoll.
30149             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
30150                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
30151                 bodyEl.dom.style.overflowY = 'auto';
30152             } else {
30153                 bodyEl.dom.style.height = '';
30154                 bodyEl.dom.style.overflowY = '';
30155             }
30156             
30157             dlg.setContentSize(w, bodyEl.getHeight());
30158             if(dlg.isVisible()){
30159                 dlg.fixedcenter = true;
30160             }
30161             return this;
30162         },
30163
30164         /**
30165          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
30166          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
30167          * @param {Number} value Any number between 0 and 1 (e.g., .5)
30168          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
30169          * @return {Roo.MessageBox} This message box
30170          */
30171         updateProgress : function(value, text){
30172             if(text){
30173                 this.updateText(text);
30174             }
30175             if (pp) { // weird bug on my firefox - for some reason this is not defined
30176                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
30177             }
30178             return this;
30179         },        
30180
30181         /**
30182          * Returns true if the message box is currently displayed
30183          * @return {Boolean} True if the message box is visible, else false
30184          */
30185         isVisible : function(){
30186             return dlg && dlg.isVisible();  
30187         },
30188
30189         /**
30190          * Hides the message box if it is displayed
30191          */
30192         hide : function(){
30193             if(this.isVisible()){
30194                 dlg.hide();
30195             }  
30196         },
30197
30198         /**
30199          * Displays a new message box, or reinitializes an existing message box, based on the config options
30200          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
30201          * The following config object properties are supported:
30202          * <pre>
30203 Property    Type             Description
30204 ----------  ---------------  ------------------------------------------------------------------------------------
30205 animEl            String/Element   An id or Element from which the message box should animate as it opens and
30206                                    closes (defaults to undefined)
30207 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
30208                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
30209 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
30210                                    progress and wait dialogs will ignore this property and always hide the
30211                                    close button as they can only be closed programmatically.
30212 cls               String           A custom CSS class to apply to the message box element
30213 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
30214                                    displayed (defaults to 75)
30215 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
30216                                    function will be btn (the name of the button that was clicked, if applicable,
30217                                    e.g. "ok"), and text (the value of the active text field, if applicable).
30218                                    Progress and wait dialogs will ignore this option since they do not respond to
30219                                    user actions and can only be closed programmatically, so any required function
30220                                    should be called by the same code after it closes the dialog.
30221 icon              String           A CSS class that provides a background image to be used as an icon for
30222                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
30223 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
30224 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
30225 modal             Boolean          False to allow user interaction with the page while the message box is
30226                                    displayed (defaults to true)
30227 msg               String           A string that will replace the existing message box body text (defaults
30228                                    to the XHTML-compliant non-breaking space character '&#160;')
30229 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
30230 progress          Boolean          True to display a progress bar (defaults to false)
30231 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
30232 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
30233 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
30234 title             String           The title text
30235 value             String           The string value to set into the active textbox element if displayed
30236 wait              Boolean          True to display a progress bar (defaults to false)
30237 width             Number           The width of the dialog in pixels
30238 </pre>
30239          *
30240          * Example usage:
30241          * <pre><code>
30242 Roo.Msg.show({
30243    title: 'Address',
30244    msg: 'Please enter your address:',
30245    width: 300,
30246    buttons: Roo.MessageBox.OKCANCEL,
30247    multiline: true,
30248    fn: saveAddress,
30249    animEl: 'addAddressBtn'
30250 });
30251 </code></pre>
30252          * @param {Object} config Configuration options
30253          * @return {Roo.MessageBox} This message box
30254          */
30255         show : function(options)
30256         {
30257             
30258             // this causes nightmares if you show one dialog after another
30259             // especially on callbacks..
30260              
30261             if(this.isVisible()){
30262                 
30263                 this.hide();
30264                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML )
30265                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
30266                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
30267                 
30268             }
30269             var d = this.getDialog();
30270             opt = options;
30271             d.setTitle(opt.title || "&#160;");
30272             d.close.setDisplayed(opt.closable !== false);
30273             activeTextEl = textboxEl;
30274             opt.prompt = opt.prompt || (opt.multiline ? true : false);
30275             if(opt.prompt){
30276                 if(opt.multiline){
30277                     textboxEl.hide();
30278                     textareaEl.show();
30279                     textareaEl.setHeight(typeof opt.multiline == "number" ?
30280                         opt.multiline : this.defaultTextHeight);
30281                     activeTextEl = textareaEl;
30282                 }else{
30283                     textboxEl.show();
30284                     textareaEl.hide();
30285                 }
30286             }else{
30287                 textboxEl.hide();
30288                 textareaEl.hide();
30289             }
30290             progressEl.setDisplayed(opt.progress === true);
30291             this.updateProgress(0);
30292             activeTextEl.dom.value = opt.value || "";
30293             if(opt.prompt){
30294                 dlg.setDefaultButton(activeTextEl);
30295             }else{
30296                 var bs = opt.buttons;
30297                 var db = null;
30298                 if(bs && bs.ok){
30299                     db = buttons["ok"];
30300                 }else if(bs && bs.yes){
30301                     db = buttons["yes"];
30302                 }
30303                 dlg.setDefaultButton(db);
30304             }
30305             bwidth = updateButtons(opt.buttons);
30306             this.updateText(opt.msg);
30307             if(opt.cls){
30308                 d.el.addClass(opt.cls);
30309             }
30310             d.proxyDrag = opt.proxyDrag === true;
30311             d.modal = opt.modal !== false;
30312             d.mask = opt.modal !== false ? mask : false;
30313             if(!d.isVisible()){
30314                 // force it to the end of the z-index stack so it gets a cursor in FF
30315                 document.body.appendChild(dlg.el.dom);
30316                 d.animateTarget = null;
30317                 d.show(options.animEl);
30318             }
30319             return this;
30320         },
30321
30322         /**
30323          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
30324          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
30325          * and closing the message box when the process is complete.
30326          * @param {String} title The title bar text
30327          * @param {String} msg The message box body text
30328          * @return {Roo.MessageBox} This message box
30329          */
30330         progress : function(title, msg){
30331             this.show({
30332                 title : title,
30333                 msg : msg,
30334                 buttons: false,
30335                 progress:true,
30336                 closable:false,
30337                 minWidth: this.minProgressWidth,
30338                 modal : true
30339             });
30340             return this;
30341         },
30342
30343         /**
30344          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
30345          * If a callback function is passed it will be called after the user clicks the button, and the
30346          * id of the button that was clicked will be passed as the only parameter to the callback
30347          * (could also be the top-right close button).
30348          * @param {String} title The title bar text
30349          * @param {String} msg The message box body text
30350          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30351          * @param {Object} scope (optional) The scope of the callback function
30352          * @return {Roo.MessageBox} This message box
30353          */
30354         alert : function(title, msg, fn, scope){
30355             this.show({
30356                 title : title,
30357                 msg : msg,
30358                 buttons: this.OK,
30359                 fn: fn,
30360                 scope : scope,
30361                 modal : true
30362             });
30363             return this;
30364         },
30365
30366         /**
30367          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
30368          * interaction while waiting for a long-running process to complete that does not have defined intervals.
30369          * You are responsible for closing the message box when the process is complete.
30370          * @param {String} msg The message box body text
30371          * @param {String} title (optional) The title bar text
30372          * @return {Roo.MessageBox} This message box
30373          */
30374         wait : function(msg, title){
30375             this.show({
30376                 title : title,
30377                 msg : msg,
30378                 buttons: false,
30379                 closable:false,
30380                 progress:true,
30381                 modal:true,
30382                 width:300,
30383                 wait:true
30384             });
30385             waitTimer = Roo.TaskMgr.start({
30386                 run: function(i){
30387                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
30388                 },
30389                 interval: 1000
30390             });
30391             return this;
30392         },
30393
30394         /**
30395          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
30396          * If a callback function is passed it will be called after the user clicks either button, and the id of the
30397          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
30398          * @param {String} title The title bar text
30399          * @param {String} msg The message box body text
30400          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30401          * @param {Object} scope (optional) The scope of the callback function
30402          * @return {Roo.MessageBox} This message box
30403          */
30404         confirm : function(title, msg, fn, scope){
30405             this.show({
30406                 title : title,
30407                 msg : msg,
30408                 buttons: this.YESNO,
30409                 fn: fn,
30410                 scope : scope,
30411                 modal : true
30412             });
30413             return this;
30414         },
30415
30416         /**
30417          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
30418          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
30419          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
30420          * (could also be the top-right close button) and the text that was entered will be passed as the two
30421          * parameters to the callback.
30422          * @param {String} title The title bar text
30423          * @param {String} msg The message box body text
30424          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30425          * @param {Object} scope (optional) The scope of the callback function
30426          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
30427          * property, or the height in pixels to create the textbox (defaults to false / single-line)
30428          * @return {Roo.MessageBox} This message box
30429          */
30430         prompt : function(title, msg, fn, scope, multiline){
30431             this.show({
30432                 title : title,
30433                 msg : msg,
30434                 buttons: this.OKCANCEL,
30435                 fn: fn,
30436                 minWidth:250,
30437                 scope : scope,
30438                 prompt:true,
30439                 multiline: multiline,
30440                 modal : true
30441             });
30442             return this;
30443         },
30444
30445         /**
30446          * Button config that displays a single OK button
30447          * @type Object
30448          */
30449         OK : {ok:true},
30450         /**
30451          * Button config that displays Yes and No buttons
30452          * @type Object
30453          */
30454         YESNO : {yes:true, no:true},
30455         /**
30456          * Button config that displays OK and Cancel buttons
30457          * @type Object
30458          */
30459         OKCANCEL : {ok:true, cancel:true},
30460         /**
30461          * Button config that displays Yes, No and Cancel buttons
30462          * @type Object
30463          */
30464         YESNOCANCEL : {yes:true, no:true, cancel:true},
30465
30466         /**
30467          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
30468          * @type Number
30469          */
30470         defaultTextHeight : 75,
30471         /**
30472          * The maximum width in pixels of the message box (defaults to 600)
30473          * @type Number
30474          */
30475         maxWidth : 600,
30476         /**
30477          * The minimum width in pixels of the message box (defaults to 100)
30478          * @type Number
30479          */
30480         minWidth : 100,
30481         /**
30482          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
30483          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
30484          * @type Number
30485          */
30486         minProgressWidth : 250,
30487         /**
30488          * An object containing the default button text strings that can be overriden for localized language support.
30489          * Supported properties are: ok, cancel, yes and no.
30490          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
30491          * @type Object
30492          */
30493         buttonText : {
30494             ok : "OK",
30495             cancel : "Cancel",
30496             yes : "Yes",
30497             no : "No"
30498         }
30499     };
30500 }();
30501
30502 /**
30503  * Shorthand for {@link Roo.MessageBox}
30504  */
30505 Roo.Msg = Roo.MessageBox;/*
30506  * Based on:
30507  * Ext JS Library 1.1.1
30508  * Copyright(c) 2006-2007, Ext JS, LLC.
30509  *
30510  * Originally Released Under LGPL - original licence link has changed is not relivant.
30511  *
30512  * Fork - LGPL
30513  * <script type="text/javascript">
30514  */
30515 /**
30516  * @class Roo.QuickTips
30517  * Provides attractive and customizable tooltips for any element.
30518  * @singleton
30519  */
30520 Roo.QuickTips = function(){
30521     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
30522     var ce, bd, xy, dd;
30523     var visible = false, disabled = true, inited = false;
30524     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
30525     
30526     var onOver = function(e){
30527         if(disabled){
30528             return;
30529         }
30530         var t = e.getTarget();
30531         if(!t || t.nodeType !== 1 || t == document || t == document.body){
30532             return;
30533         }
30534         if(ce && t == ce.el){
30535             clearTimeout(hideProc);
30536             return;
30537         }
30538         if(t && tagEls[t.id]){
30539             tagEls[t.id].el = t;
30540             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
30541             return;
30542         }
30543         var ttp, et = Roo.fly(t);
30544         var ns = cfg.namespace;
30545         if(tm.interceptTitles && t.title){
30546             ttp = t.title;
30547             t.qtip = ttp;
30548             t.removeAttribute("title");
30549             e.preventDefault();
30550         }else{
30551             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
30552         }
30553         if(ttp){
30554             showProc = show.defer(tm.showDelay, tm, [{
30555                 el: t, 
30556                 text: ttp, 
30557                 width: et.getAttributeNS(ns, cfg.width),
30558                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
30559                 title: et.getAttributeNS(ns, cfg.title),
30560                     cls: et.getAttributeNS(ns, cfg.cls)
30561             }]);
30562         }
30563     };
30564     
30565     var onOut = function(e){
30566         clearTimeout(showProc);
30567         var t = e.getTarget();
30568         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
30569             hideProc = setTimeout(hide, tm.hideDelay);
30570         }
30571     };
30572     
30573     var onMove = function(e){
30574         if(disabled){
30575             return;
30576         }
30577         xy = e.getXY();
30578         xy[1] += 18;
30579         if(tm.trackMouse && ce){
30580             el.setXY(xy);
30581         }
30582     };
30583     
30584     var onDown = function(e){
30585         clearTimeout(showProc);
30586         clearTimeout(hideProc);
30587         if(!e.within(el)){
30588             if(tm.hideOnClick){
30589                 hide();
30590                 tm.disable();
30591                 tm.enable.defer(100, tm);
30592             }
30593         }
30594     };
30595     
30596     var getPad = function(){
30597         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
30598     };
30599
30600     var show = function(o){
30601         if(disabled){
30602             return;
30603         }
30604         clearTimeout(dismissProc);
30605         ce = o;
30606         if(removeCls){ // in case manually hidden
30607             el.removeClass(removeCls);
30608             removeCls = null;
30609         }
30610         if(ce.cls){
30611             el.addClass(ce.cls);
30612             removeCls = ce.cls;
30613         }
30614         if(ce.title){
30615             tipTitle.update(ce.title);
30616             tipTitle.show();
30617         }else{
30618             tipTitle.update('');
30619             tipTitle.hide();
30620         }
30621         el.dom.style.width  = tm.maxWidth+'px';
30622         //tipBody.dom.style.width = '';
30623         tipBodyText.update(o.text);
30624         var p = getPad(), w = ce.width;
30625         if(!w){
30626             var td = tipBodyText.dom;
30627             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
30628             if(aw > tm.maxWidth){
30629                 w = tm.maxWidth;
30630             }else if(aw < tm.minWidth){
30631                 w = tm.minWidth;
30632             }else{
30633                 w = aw;
30634             }
30635         }
30636         //tipBody.setWidth(w);
30637         el.setWidth(parseInt(w, 10) + p);
30638         if(ce.autoHide === false){
30639             close.setDisplayed(true);
30640             if(dd){
30641                 dd.unlock();
30642             }
30643         }else{
30644             close.setDisplayed(false);
30645             if(dd){
30646                 dd.lock();
30647             }
30648         }
30649         if(xy){
30650             el.avoidY = xy[1]-18;
30651             el.setXY(xy);
30652         }
30653         if(tm.animate){
30654             el.setOpacity(.1);
30655             el.setStyle("visibility", "visible");
30656             el.fadeIn({callback: afterShow});
30657         }else{
30658             afterShow();
30659         }
30660     };
30661     
30662     var afterShow = function(){
30663         if(ce){
30664             el.show();
30665             esc.enable();
30666             if(tm.autoDismiss && ce.autoHide !== false){
30667                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
30668             }
30669         }
30670     };
30671     
30672     var hide = function(noanim){
30673         clearTimeout(dismissProc);
30674         clearTimeout(hideProc);
30675         ce = null;
30676         if(el.isVisible()){
30677             esc.disable();
30678             if(noanim !== true && tm.animate){
30679                 el.fadeOut({callback: afterHide});
30680             }else{
30681                 afterHide();
30682             } 
30683         }
30684     };
30685     
30686     var afterHide = function(){
30687         el.hide();
30688         if(removeCls){
30689             el.removeClass(removeCls);
30690             removeCls = null;
30691         }
30692     };
30693     
30694     return {
30695         /**
30696         * @cfg {Number} minWidth
30697         * The minimum width of the quick tip (defaults to 40)
30698         */
30699        minWidth : 40,
30700         /**
30701         * @cfg {Number} maxWidth
30702         * The maximum width of the quick tip (defaults to 300)
30703         */
30704        maxWidth : 300,
30705         /**
30706         * @cfg {Boolean} interceptTitles
30707         * True to automatically use the element's DOM title value if available (defaults to false)
30708         */
30709        interceptTitles : false,
30710         /**
30711         * @cfg {Boolean} trackMouse
30712         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
30713         */
30714        trackMouse : false,
30715         /**
30716         * @cfg {Boolean} hideOnClick
30717         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
30718         */
30719        hideOnClick : true,
30720         /**
30721         * @cfg {Number} showDelay
30722         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
30723         */
30724        showDelay : 500,
30725         /**
30726         * @cfg {Number} hideDelay
30727         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
30728         */
30729        hideDelay : 200,
30730         /**
30731         * @cfg {Boolean} autoHide
30732         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
30733         * Used in conjunction with hideDelay.
30734         */
30735        autoHide : true,
30736         /**
30737         * @cfg {Boolean}
30738         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
30739         * (defaults to true).  Used in conjunction with autoDismissDelay.
30740         */
30741        autoDismiss : true,
30742         /**
30743         * @cfg {Number}
30744         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
30745         */
30746        autoDismissDelay : 5000,
30747        /**
30748         * @cfg {Boolean} animate
30749         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
30750         */
30751        animate : false,
30752
30753        /**
30754         * @cfg {String} title
30755         * Title text to display (defaults to '').  This can be any valid HTML markup.
30756         */
30757         title: '',
30758        /**
30759         * @cfg {String} text
30760         * Body text to display (defaults to '').  This can be any valid HTML markup.
30761         */
30762         text : '',
30763        /**
30764         * @cfg {String} cls
30765         * A CSS class to apply to the base quick tip element (defaults to '').
30766         */
30767         cls : '',
30768        /**
30769         * @cfg {Number} width
30770         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
30771         * minWidth or maxWidth.
30772         */
30773         width : null,
30774
30775     /**
30776      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
30777      * or display QuickTips in a page.
30778      */
30779        init : function(){
30780           tm = Roo.QuickTips;
30781           cfg = tm.tagConfig;
30782           if(!inited){
30783               if(!Roo.isReady){ // allow calling of init() before onReady
30784                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
30785                   return;
30786               }
30787               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
30788               el.fxDefaults = {stopFx: true};
30789               // maximum custom styling
30790               //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>');
30791               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>');              
30792               tipTitle = el.child('h3');
30793               tipTitle.enableDisplayMode("block");
30794               tipBody = el.child('div.x-tip-bd');
30795               tipBodyText = el.child('div.x-tip-bd-inner');
30796               //bdLeft = el.child('div.x-tip-bd-left');
30797               //bdRight = el.child('div.x-tip-bd-right');
30798               close = el.child('div.x-tip-close');
30799               close.enableDisplayMode("block");
30800               close.on("click", hide);
30801               var d = Roo.get(document);
30802               d.on("mousedown", onDown);
30803               d.on("mouseover", onOver);
30804               d.on("mouseout", onOut);
30805               d.on("mousemove", onMove);
30806               esc = d.addKeyListener(27, hide);
30807               esc.disable();
30808               if(Roo.dd.DD){
30809                   dd = el.initDD("default", null, {
30810                       onDrag : function(){
30811                           el.sync();  
30812                       }
30813                   });
30814                   dd.setHandleElId(tipTitle.id);
30815                   dd.lock();
30816               }
30817               inited = true;
30818           }
30819           this.enable(); 
30820        },
30821
30822     /**
30823      * Configures a new quick tip instance and assigns it to a target element.  The following config options
30824      * are supported:
30825      * <pre>
30826 Property    Type                   Description
30827 ----------  ---------------------  ------------------------------------------------------------------------
30828 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
30829      * </ul>
30830      * @param {Object} config The config object
30831      */
30832        register : function(config){
30833            var cs = config instanceof Array ? config : arguments;
30834            for(var i = 0, len = cs.length; i < len; i++) {
30835                var c = cs[i];
30836                var target = c.target;
30837                if(target){
30838                    if(target instanceof Array){
30839                        for(var j = 0, jlen = target.length; j < jlen; j++){
30840                            tagEls[target[j]] = c;
30841                        }
30842                    }else{
30843                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
30844                    }
30845                }
30846            }
30847        },
30848
30849     /**
30850      * Removes this quick tip from its element and destroys it.
30851      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
30852      */
30853        unregister : function(el){
30854            delete tagEls[Roo.id(el)];
30855        },
30856
30857     /**
30858      * Enable this quick tip.
30859      */
30860        enable : function(){
30861            if(inited && disabled){
30862                locks.pop();
30863                if(locks.length < 1){
30864                    disabled = false;
30865                }
30866            }
30867        },
30868
30869     /**
30870      * Disable this quick tip.
30871      */
30872        disable : function(){
30873           disabled = true;
30874           clearTimeout(showProc);
30875           clearTimeout(hideProc);
30876           clearTimeout(dismissProc);
30877           if(ce){
30878               hide(true);
30879           }
30880           locks.push(1);
30881        },
30882
30883     /**
30884      * Returns true if the quick tip is enabled, else false.
30885      */
30886        isEnabled : function(){
30887             return !disabled;
30888        },
30889
30890         // private
30891        tagConfig : {
30892            namespace : "ext",
30893            attribute : "qtip",
30894            width : "width",
30895            target : "target",
30896            title : "qtitle",
30897            hide : "hide",
30898            cls : "qclass"
30899        }
30900    };
30901 }();
30902
30903 // backwards compat
30904 Roo.QuickTips.tips = Roo.QuickTips.register;/*
30905  * Based on:
30906  * Ext JS Library 1.1.1
30907  * Copyright(c) 2006-2007, Ext JS, LLC.
30908  *
30909  * Originally Released Under LGPL - original licence link has changed is not relivant.
30910  *
30911  * Fork - LGPL
30912  * <script type="text/javascript">
30913  */
30914  
30915
30916 /**
30917  * @class Roo.tree.TreePanel
30918  * @extends Roo.data.Tree
30919
30920  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
30921  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
30922  * @cfg {Boolean} enableDD true to enable drag and drop
30923  * @cfg {Boolean} enableDrag true to enable just drag
30924  * @cfg {Boolean} enableDrop true to enable just drop
30925  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
30926  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
30927  * @cfg {String} ddGroup The DD group this TreePanel belongs to
30928  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
30929  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
30930  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
30931  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
30932  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
30933  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
30934  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
30935  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
30936  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
30937  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
30938  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
30939  * @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>
30940  * @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>
30941  * 
30942  * @constructor
30943  * @param {String/HTMLElement/Element} el The container element
30944  * @param {Object} config
30945  */
30946 Roo.tree.TreePanel = function(el, config){
30947     var root = false;
30948     var loader = false;
30949     if (config.root) {
30950         root = config.root;
30951         delete config.root;
30952     }
30953     if (config.loader) {
30954         loader = config.loader;
30955         delete config.loader;
30956     }
30957     
30958     Roo.apply(this, config);
30959     Roo.tree.TreePanel.superclass.constructor.call(this);
30960     this.el = Roo.get(el);
30961     this.el.addClass('x-tree');
30962     //console.log(root);
30963     if (root) {
30964         this.setRootNode( Roo.factory(root, Roo.tree));
30965     }
30966     if (loader) {
30967         this.loader = Roo.factory(loader, Roo.tree);
30968     }
30969    /**
30970     * Read-only. The id of the container element becomes this TreePanel's id.
30971     */
30972     this.id = this.el.id;
30973     this.addEvents({
30974         /**
30975         * @event beforeload
30976         * Fires before a node is loaded, return false to cancel
30977         * @param {Node} node The node being loaded
30978         */
30979         "beforeload" : true,
30980         /**
30981         * @event load
30982         * Fires when a node is loaded
30983         * @param {Node} node The node that was loaded
30984         */
30985         "load" : true,
30986         /**
30987         * @event textchange
30988         * Fires when the text for a node is changed
30989         * @param {Node} node The node
30990         * @param {String} text The new text
30991         * @param {String} oldText The old text
30992         */
30993         "textchange" : true,
30994         /**
30995         * @event beforeexpand
30996         * Fires before a node is expanded, return false to cancel.
30997         * @param {Node} node The node
30998         * @param {Boolean} deep
30999         * @param {Boolean} anim
31000         */
31001         "beforeexpand" : true,
31002         /**
31003         * @event beforecollapse
31004         * Fires before a node is collapsed, return false to cancel.
31005         * @param {Node} node The node
31006         * @param {Boolean} deep
31007         * @param {Boolean} anim
31008         */
31009         "beforecollapse" : true,
31010         /**
31011         * @event expand
31012         * Fires when a node is expanded
31013         * @param {Node} node The node
31014         */
31015         "expand" : true,
31016         /**
31017         * @event disabledchange
31018         * Fires when the disabled status of a node changes
31019         * @param {Node} node The node
31020         * @param {Boolean} disabled
31021         */
31022         "disabledchange" : true,
31023         /**
31024         * @event collapse
31025         * Fires when a node is collapsed
31026         * @param {Node} node The node
31027         */
31028         "collapse" : true,
31029         /**
31030         * @event beforeclick
31031         * Fires before click processing on a node. Return false to cancel the default action.
31032         * @param {Node} node The node
31033         * @param {Roo.EventObject} e The event object
31034         */
31035         "beforeclick":true,
31036         /**
31037         * @event checkchange
31038         * Fires when a node with a checkbox's checked property changes
31039         * @param {Node} this This node
31040         * @param {Boolean} checked
31041         */
31042         "checkchange":true,
31043         /**
31044         * @event click
31045         * Fires when a node is clicked
31046         * @param {Node} node The node
31047         * @param {Roo.EventObject} e The event object
31048         */
31049         "click":true,
31050         /**
31051         * @event dblclick
31052         * Fires when a node is double clicked
31053         * @param {Node} node The node
31054         * @param {Roo.EventObject} e The event object
31055         */
31056         "dblclick":true,
31057         /**
31058         * @event contextmenu
31059         * Fires when a node is right clicked
31060         * @param {Node} node The node
31061         * @param {Roo.EventObject} e The event object
31062         */
31063         "contextmenu":true,
31064         /**
31065         * @event beforechildrenrendered
31066         * Fires right before the child nodes for a node are rendered
31067         * @param {Node} node The node
31068         */
31069         "beforechildrenrendered":true,
31070         /**
31071         * @event startdrag
31072         * Fires when a node starts being dragged
31073         * @param {Roo.tree.TreePanel} this
31074         * @param {Roo.tree.TreeNode} node
31075         * @param {event} e The raw browser event
31076         */ 
31077        "startdrag" : true,
31078        /**
31079         * @event enddrag
31080         * Fires when a drag operation is complete
31081         * @param {Roo.tree.TreePanel} this
31082         * @param {Roo.tree.TreeNode} node
31083         * @param {event} e The raw browser event
31084         */
31085        "enddrag" : true,
31086        /**
31087         * @event dragdrop
31088         * Fires when a dragged node is dropped on a valid DD target
31089         * @param {Roo.tree.TreePanel} this
31090         * @param {Roo.tree.TreeNode} node
31091         * @param {DD} dd The dd it was dropped on
31092         * @param {event} e The raw browser event
31093         */
31094        "dragdrop" : true,
31095        /**
31096         * @event beforenodedrop
31097         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
31098         * passed to handlers has the following properties:<br />
31099         * <ul style="padding:5px;padding-left:16px;">
31100         * <li>tree - The TreePanel</li>
31101         * <li>target - The node being targeted for the drop</li>
31102         * <li>data - The drag data from the drag source</li>
31103         * <li>point - The point of the drop - append, above or below</li>
31104         * <li>source - The drag source</li>
31105         * <li>rawEvent - Raw mouse event</li>
31106         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
31107         * to be inserted by setting them on this object.</li>
31108         * <li>cancel - Set this to true to cancel the drop.</li>
31109         * </ul>
31110         * @param {Object} dropEvent
31111         */
31112        "beforenodedrop" : true,
31113        /**
31114         * @event nodedrop
31115         * Fires after a DD object is dropped on a node in this tree. The dropEvent
31116         * passed to handlers has the following properties:<br />
31117         * <ul style="padding:5px;padding-left:16px;">
31118         * <li>tree - The TreePanel</li>
31119         * <li>target - The node being targeted for the drop</li>
31120         * <li>data - The drag data from the drag source</li>
31121         * <li>point - The point of the drop - append, above or below</li>
31122         * <li>source - The drag source</li>
31123         * <li>rawEvent - Raw mouse event</li>
31124         * <li>dropNode - Dropped node(s).</li>
31125         * </ul>
31126         * @param {Object} dropEvent
31127         */
31128        "nodedrop" : true,
31129         /**
31130         * @event nodedragover
31131         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
31132         * passed to handlers has the following properties:<br />
31133         * <ul style="padding:5px;padding-left:16px;">
31134         * <li>tree - The TreePanel</li>
31135         * <li>target - The node being targeted for the drop</li>
31136         * <li>data - The drag data from the drag source</li>
31137         * <li>point - The point of the drop - append, above or below</li>
31138         * <li>source - The drag source</li>
31139         * <li>rawEvent - Raw mouse event</li>
31140         * <li>dropNode - Drop node(s) provided by the source.</li>
31141         * <li>cancel - Set this to true to signal drop not allowed.</li>
31142         * </ul>
31143         * @param {Object} dragOverEvent
31144         */
31145        "nodedragover" : true
31146         
31147     });
31148     if(this.singleExpand){
31149        this.on("beforeexpand", this.restrictExpand, this);
31150     }
31151     if (this.editor) {
31152         this.editor.tree = this;
31153         this.editor = Roo.factory(this.editor, Roo.tree);
31154     }
31155     
31156     if (this.selModel) {
31157         this.selModel = Roo.factory(this.selModel, Roo.tree);
31158     }
31159    
31160 };
31161 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
31162     rootVisible : true,
31163     animate: Roo.enableFx,
31164     lines : true,
31165     enableDD : false,
31166     hlDrop : Roo.enableFx,
31167   
31168     renderer: false,
31169     
31170     rendererTip: false,
31171     // private
31172     restrictExpand : function(node){
31173         var p = node.parentNode;
31174         if(p){
31175             if(p.expandedChild && p.expandedChild.parentNode == p){
31176                 p.expandedChild.collapse();
31177             }
31178             p.expandedChild = node;
31179         }
31180     },
31181
31182     // private override
31183     setRootNode : function(node){
31184         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
31185         if(!this.rootVisible){
31186             node.ui = new Roo.tree.RootTreeNodeUI(node);
31187         }
31188         return node;
31189     },
31190
31191     /**
31192      * Returns the container element for this TreePanel
31193      */
31194     getEl : function(){
31195         return this.el;
31196     },
31197
31198     /**
31199      * Returns the default TreeLoader for this TreePanel
31200      */
31201     getLoader : function(){
31202         return this.loader;
31203     },
31204
31205     /**
31206      * Expand all nodes
31207      */
31208     expandAll : function(){
31209         this.root.expand(true);
31210     },
31211
31212     /**
31213      * Collapse all nodes
31214      */
31215     collapseAll : function(){
31216         this.root.collapse(true);
31217     },
31218
31219     /**
31220      * Returns the selection model used by this TreePanel
31221      */
31222     getSelectionModel : function(){
31223         if(!this.selModel){
31224             this.selModel = new Roo.tree.DefaultSelectionModel();
31225         }
31226         return this.selModel;
31227     },
31228
31229     /**
31230      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
31231      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
31232      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
31233      * @return {Array}
31234      */
31235     getChecked : function(a, startNode){
31236         startNode = startNode || this.root;
31237         var r = [];
31238         var f = function(){
31239             if(this.attributes.checked){
31240                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
31241             }
31242         }
31243         startNode.cascade(f);
31244         return r;
31245     },
31246
31247     /**
31248      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
31249      * @param {String} path
31250      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
31251      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
31252      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
31253      */
31254     expandPath : function(path, attr, callback){
31255         attr = attr || "id";
31256         var keys = path.split(this.pathSeparator);
31257         var curNode = this.root;
31258         if(curNode.attributes[attr] != keys[1]){ // invalid root
31259             if(callback){
31260                 callback(false, null);
31261             }
31262             return;
31263         }
31264         var index = 1;
31265         var f = function(){
31266             if(++index == keys.length){
31267                 if(callback){
31268                     callback(true, curNode);
31269                 }
31270                 return;
31271             }
31272             var c = curNode.findChild(attr, keys[index]);
31273             if(!c){
31274                 if(callback){
31275                     callback(false, curNode);
31276                 }
31277                 return;
31278             }
31279             curNode = c;
31280             c.expand(false, false, f);
31281         };
31282         curNode.expand(false, false, f);
31283     },
31284
31285     /**
31286      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
31287      * @param {String} path
31288      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
31289      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
31290      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
31291      */
31292     selectPath : function(path, attr, callback){
31293         attr = attr || "id";
31294         var keys = path.split(this.pathSeparator);
31295         var v = keys.pop();
31296         if(keys.length > 0){
31297             var f = function(success, node){
31298                 if(success && node){
31299                     var n = node.findChild(attr, v);
31300                     if(n){
31301                         n.select();
31302                         if(callback){
31303                             callback(true, n);
31304                         }
31305                     }else if(callback){
31306                         callback(false, n);
31307                     }
31308                 }else{
31309                     if(callback){
31310                         callback(false, n);
31311                     }
31312                 }
31313             };
31314             this.expandPath(keys.join(this.pathSeparator), attr, f);
31315         }else{
31316             this.root.select();
31317             if(callback){
31318                 callback(true, this.root);
31319             }
31320         }
31321     },
31322
31323     getTreeEl : function(){
31324         return this.el;
31325     },
31326
31327     /**
31328      * Trigger rendering of this TreePanel
31329      */
31330     render : function(){
31331         if (this.innerCt) {
31332             return this; // stop it rendering more than once!!
31333         }
31334         
31335         this.innerCt = this.el.createChild({tag:"ul",
31336                cls:"x-tree-root-ct " +
31337                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
31338
31339         if(this.containerScroll){
31340             Roo.dd.ScrollManager.register(this.el);
31341         }
31342         if((this.enableDD || this.enableDrop) && !this.dropZone){
31343            /**
31344             * The dropZone used by this tree if drop is enabled
31345             * @type Roo.tree.TreeDropZone
31346             */
31347              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
31348                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
31349            });
31350         }
31351         if((this.enableDD || this.enableDrag) && !this.dragZone){
31352            /**
31353             * The dragZone used by this tree if drag is enabled
31354             * @type Roo.tree.TreeDragZone
31355             */
31356             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
31357                ddGroup: this.ddGroup || "TreeDD",
31358                scroll: this.ddScroll
31359            });
31360         }
31361         this.getSelectionModel().init(this);
31362         if (!this.root) {
31363             console.log("ROOT not set in tree");
31364             return;
31365         }
31366         this.root.render();
31367         if(!this.rootVisible){
31368             this.root.renderChildren();
31369         }
31370         return this;
31371     }
31372 });/*
31373  * Based on:
31374  * Ext JS Library 1.1.1
31375  * Copyright(c) 2006-2007, Ext JS, LLC.
31376  *
31377  * Originally Released Under LGPL - original licence link has changed is not relivant.
31378  *
31379  * Fork - LGPL
31380  * <script type="text/javascript">
31381  */
31382  
31383
31384 /**
31385  * @class Roo.tree.DefaultSelectionModel
31386  * @extends Roo.util.Observable
31387  * The default single selection for a TreePanel.
31388  * @param {Object} cfg Configuration
31389  */
31390 Roo.tree.DefaultSelectionModel = function(cfg){
31391    this.selNode = null;
31392    
31393    
31394    
31395    this.addEvents({
31396        /**
31397         * @event selectionchange
31398         * Fires when the selected node changes
31399         * @param {DefaultSelectionModel} this
31400         * @param {TreeNode} node the new selection
31401         */
31402        "selectionchange" : true,
31403
31404        /**
31405         * @event beforeselect
31406         * Fires before the selected node changes, return false to cancel the change
31407         * @param {DefaultSelectionModel} this
31408         * @param {TreeNode} node the new selection
31409         * @param {TreeNode} node the old selection
31410         */
31411        "beforeselect" : true
31412    });
31413    
31414     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
31415 };
31416
31417 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
31418     init : function(tree){
31419         this.tree = tree;
31420         tree.getTreeEl().on("keydown", this.onKeyDown, this);
31421         tree.on("click", this.onNodeClick, this);
31422     },
31423     
31424     onNodeClick : function(node, e){
31425         if (e.ctrlKey && this.selNode == node)  {
31426             this.unselect(node);
31427             return;
31428         }
31429         this.select(node);
31430     },
31431     
31432     /**
31433      * Select a node.
31434      * @param {TreeNode} node The node to select
31435      * @return {TreeNode} The selected node
31436      */
31437     select : function(node){
31438         var last = this.selNode;
31439         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
31440             if(last){
31441                 last.ui.onSelectedChange(false);
31442             }
31443             this.selNode = node;
31444             node.ui.onSelectedChange(true);
31445             this.fireEvent("selectionchange", this, node, last);
31446         }
31447         return node;
31448     },
31449     
31450     /**
31451      * Deselect a node.
31452      * @param {TreeNode} node The node to unselect
31453      */
31454     unselect : function(node){
31455         if(this.selNode == node){
31456             this.clearSelections();
31457         }    
31458     },
31459     
31460     /**
31461      * Clear all selections
31462      */
31463     clearSelections : function(){
31464         var n = this.selNode;
31465         if(n){
31466             n.ui.onSelectedChange(false);
31467             this.selNode = null;
31468             this.fireEvent("selectionchange", this, null);
31469         }
31470         return n;
31471     },
31472     
31473     /**
31474      * Get the selected node
31475      * @return {TreeNode} The selected node
31476      */
31477     getSelectedNode : function(){
31478         return this.selNode;    
31479     },
31480     
31481     /**
31482      * Returns true if the node is selected
31483      * @param {TreeNode} node The node to check
31484      * @return {Boolean}
31485      */
31486     isSelected : function(node){
31487         return this.selNode == node;  
31488     },
31489
31490     /**
31491      * Selects the node above the selected node in the tree, intelligently walking the nodes
31492      * @return TreeNode The new selection
31493      */
31494     selectPrevious : function(){
31495         var s = this.selNode || this.lastSelNode;
31496         if(!s){
31497             return null;
31498         }
31499         var ps = s.previousSibling;
31500         if(ps){
31501             if(!ps.isExpanded() || ps.childNodes.length < 1){
31502                 return this.select(ps);
31503             } else{
31504                 var lc = ps.lastChild;
31505                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
31506                     lc = lc.lastChild;
31507                 }
31508                 return this.select(lc);
31509             }
31510         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
31511             return this.select(s.parentNode);
31512         }
31513         return null;
31514     },
31515
31516     /**
31517      * Selects the node above the selected node in the tree, intelligently walking the nodes
31518      * @return TreeNode The new selection
31519      */
31520     selectNext : function(){
31521         var s = this.selNode || this.lastSelNode;
31522         if(!s){
31523             return null;
31524         }
31525         if(s.firstChild && s.isExpanded()){
31526              return this.select(s.firstChild);
31527          }else if(s.nextSibling){
31528              return this.select(s.nextSibling);
31529          }else if(s.parentNode){
31530             var newS = null;
31531             s.parentNode.bubble(function(){
31532                 if(this.nextSibling){
31533                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
31534                     return false;
31535                 }
31536             });
31537             return newS;
31538          }
31539         return null;
31540     },
31541
31542     onKeyDown : function(e){
31543         var s = this.selNode || this.lastSelNode;
31544         // undesirable, but required
31545         var sm = this;
31546         if(!s){
31547             return;
31548         }
31549         var k = e.getKey();
31550         switch(k){
31551              case e.DOWN:
31552                  e.stopEvent();
31553                  this.selectNext();
31554              break;
31555              case e.UP:
31556                  e.stopEvent();
31557                  this.selectPrevious();
31558              break;
31559              case e.RIGHT:
31560                  e.preventDefault();
31561                  if(s.hasChildNodes()){
31562                      if(!s.isExpanded()){
31563                          s.expand();
31564                      }else if(s.firstChild){
31565                          this.select(s.firstChild, e);
31566                      }
31567                  }
31568              break;
31569              case e.LEFT:
31570                  e.preventDefault();
31571                  if(s.hasChildNodes() && s.isExpanded()){
31572                      s.collapse();
31573                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
31574                      this.select(s.parentNode, e);
31575                  }
31576              break;
31577         };
31578     }
31579 });
31580
31581 /**
31582  * @class Roo.tree.MultiSelectionModel
31583  * @extends Roo.util.Observable
31584  * Multi selection for a TreePanel.
31585  * @param {Object} cfg Configuration
31586  */
31587 Roo.tree.MultiSelectionModel = function(){
31588    this.selNodes = [];
31589    this.selMap = {};
31590    this.addEvents({
31591        /**
31592         * @event selectionchange
31593         * Fires when the selected nodes change
31594         * @param {MultiSelectionModel} this
31595         * @param {Array} nodes Array of the selected nodes
31596         */
31597        "selectionchange" : true
31598    });
31599    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
31600    
31601 };
31602
31603 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
31604     init : function(tree){
31605         this.tree = tree;
31606         tree.getTreeEl().on("keydown", this.onKeyDown, this);
31607         tree.on("click", this.onNodeClick, this);
31608     },
31609     
31610     onNodeClick : function(node, e){
31611         this.select(node, e, e.ctrlKey);
31612     },
31613     
31614     /**
31615      * Select a node.
31616      * @param {TreeNode} node The node to select
31617      * @param {EventObject} e (optional) An event associated with the selection
31618      * @param {Boolean} keepExisting True to retain existing selections
31619      * @return {TreeNode} The selected node
31620      */
31621     select : function(node, e, keepExisting){
31622         if(keepExisting !== true){
31623             this.clearSelections(true);
31624         }
31625         if(this.isSelected(node)){
31626             this.lastSelNode = node;
31627             return node;
31628         }
31629         this.selNodes.push(node);
31630         this.selMap[node.id] = node;
31631         this.lastSelNode = node;
31632         node.ui.onSelectedChange(true);
31633         this.fireEvent("selectionchange", this, this.selNodes);
31634         return node;
31635     },
31636     
31637     /**
31638      * Deselect a node.
31639      * @param {TreeNode} node The node to unselect
31640      */
31641     unselect : function(node){
31642         if(this.selMap[node.id]){
31643             node.ui.onSelectedChange(false);
31644             var sn = this.selNodes;
31645             var index = -1;
31646             if(sn.indexOf){
31647                 index = sn.indexOf(node);
31648             }else{
31649                 for(var i = 0, len = sn.length; i < len; i++){
31650                     if(sn[i] == node){
31651                         index = i;
31652                         break;
31653                     }
31654                 }
31655             }
31656             if(index != -1){
31657                 this.selNodes.splice(index, 1);
31658             }
31659             delete this.selMap[node.id];
31660             this.fireEvent("selectionchange", this, this.selNodes);
31661         }
31662     },
31663     
31664     /**
31665      * Clear all selections
31666      */
31667     clearSelections : function(suppressEvent){
31668         var sn = this.selNodes;
31669         if(sn.length > 0){
31670             for(var i = 0, len = sn.length; i < len; i++){
31671                 sn[i].ui.onSelectedChange(false);
31672             }
31673             this.selNodes = [];
31674             this.selMap = {};
31675             if(suppressEvent !== true){
31676                 this.fireEvent("selectionchange", this, this.selNodes);
31677             }
31678         }
31679     },
31680     
31681     /**
31682      * Returns true if the node is selected
31683      * @param {TreeNode} node The node to check
31684      * @return {Boolean}
31685      */
31686     isSelected : function(node){
31687         return this.selMap[node.id] ? true : false;  
31688     },
31689     
31690     /**
31691      * Returns an array of the selected nodes
31692      * @return {Array}
31693      */
31694     getSelectedNodes : function(){
31695         return this.selNodes;    
31696     },
31697
31698     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
31699
31700     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
31701
31702     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
31703 });/*
31704  * Based on:
31705  * Ext JS Library 1.1.1
31706  * Copyright(c) 2006-2007, Ext JS, LLC.
31707  *
31708  * Originally Released Under LGPL - original licence link has changed is not relivant.
31709  *
31710  * Fork - LGPL
31711  * <script type="text/javascript">
31712  */
31713  
31714 /**
31715  * @class Roo.tree.TreeNode
31716  * @extends Roo.data.Node
31717  * @cfg {String} text The text for this node
31718  * @cfg {Boolean} expanded true to start the node expanded
31719  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
31720  * @cfg {Boolean} allowDrop false if this node cannot be drop on
31721  * @cfg {Boolean} disabled true to start the node disabled
31722  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
31723  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
31724  * @cfg {String} cls A css class to be added to the node
31725  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
31726  * @cfg {String} href URL of the link used for the node (defaults to #)
31727  * @cfg {String} hrefTarget target frame for the link
31728  * @cfg {String} qtip An Ext QuickTip for the node
31729  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
31730  * @cfg {Boolean} singleClickExpand True for single click expand on this node
31731  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
31732  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
31733  * (defaults to undefined with no checkbox rendered)
31734  * @constructor
31735  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
31736  */
31737 Roo.tree.TreeNode = function(attributes){
31738     attributes = attributes || {};
31739     if(typeof attributes == "string"){
31740         attributes = {text: attributes};
31741     }
31742     this.childrenRendered = false;
31743     this.rendered = false;
31744     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
31745     this.expanded = attributes.expanded === true;
31746     this.isTarget = attributes.isTarget !== false;
31747     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
31748     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
31749
31750     /**
31751      * Read-only. The text for this node. To change it use setText().
31752      * @type String
31753      */
31754     this.text = attributes.text;
31755     /**
31756      * True if this node is disabled.
31757      * @type Boolean
31758      */
31759     this.disabled = attributes.disabled === true;
31760
31761     this.addEvents({
31762         /**
31763         * @event textchange
31764         * Fires when the text for this node is changed
31765         * @param {Node} this This node
31766         * @param {String} text The new text
31767         * @param {String} oldText The old text
31768         */
31769         "textchange" : true,
31770         /**
31771         * @event beforeexpand
31772         * Fires before this node is expanded, return false to cancel.
31773         * @param {Node} this This node
31774         * @param {Boolean} deep
31775         * @param {Boolean} anim
31776         */
31777         "beforeexpand" : true,
31778         /**
31779         * @event beforecollapse
31780         * Fires before this node is collapsed, return false to cancel.
31781         * @param {Node} this This node
31782         * @param {Boolean} deep
31783         * @param {Boolean} anim
31784         */
31785         "beforecollapse" : true,
31786         /**
31787         * @event expand
31788         * Fires when this node is expanded
31789         * @param {Node} this This node
31790         */
31791         "expand" : true,
31792         /**
31793         * @event disabledchange
31794         * Fires when the disabled status of this node changes
31795         * @param {Node} this This node
31796         * @param {Boolean} disabled
31797         */
31798         "disabledchange" : true,
31799         /**
31800         * @event collapse
31801         * Fires when this node is collapsed
31802         * @param {Node} this This node
31803         */
31804         "collapse" : true,
31805         /**
31806         * @event beforeclick
31807         * Fires before click processing. Return false to cancel the default action.
31808         * @param {Node} this This node
31809         * @param {Roo.EventObject} e The event object
31810         */
31811         "beforeclick":true,
31812         /**
31813         * @event checkchange
31814         * Fires when a node with a checkbox's checked property changes
31815         * @param {Node} this This node
31816         * @param {Boolean} checked
31817         */
31818         "checkchange":true,
31819         /**
31820         * @event click
31821         * Fires when this node is clicked
31822         * @param {Node} this This node
31823         * @param {Roo.EventObject} e The event object
31824         */
31825         "click":true,
31826         /**
31827         * @event dblclick
31828         * Fires when this node is double clicked
31829         * @param {Node} this This node
31830         * @param {Roo.EventObject} e The event object
31831         */
31832         "dblclick":true,
31833         /**
31834         * @event contextmenu
31835         * Fires when this node is right clicked
31836         * @param {Node} this This node
31837         * @param {Roo.EventObject} e The event object
31838         */
31839         "contextmenu":true,
31840         /**
31841         * @event beforechildrenrendered
31842         * Fires right before the child nodes for this node are rendered
31843         * @param {Node} this This node
31844         */
31845         "beforechildrenrendered":true
31846     });
31847
31848     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
31849
31850     /**
31851      * Read-only. The UI for this node
31852      * @type TreeNodeUI
31853      */
31854     this.ui = new uiClass(this);
31855 };
31856 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
31857     preventHScroll: true,
31858     /**
31859      * Returns true if this node is expanded
31860      * @return {Boolean}
31861      */
31862     isExpanded : function(){
31863         return this.expanded;
31864     },
31865
31866     /**
31867      * Returns the UI object for this node
31868      * @return {TreeNodeUI}
31869      */
31870     getUI : function(){
31871         return this.ui;
31872     },
31873
31874     // private override
31875     setFirstChild : function(node){
31876         var of = this.firstChild;
31877         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
31878         if(this.childrenRendered && of && node != of){
31879             of.renderIndent(true, true);
31880         }
31881         if(this.rendered){
31882             this.renderIndent(true, true);
31883         }
31884     },
31885
31886     // private override
31887     setLastChild : function(node){
31888         var ol = this.lastChild;
31889         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
31890         if(this.childrenRendered && ol && node != ol){
31891             ol.renderIndent(true, true);
31892         }
31893         if(this.rendered){
31894             this.renderIndent(true, true);
31895         }
31896     },
31897
31898     // these methods are overridden to provide lazy rendering support
31899     // private override
31900     appendChild : function(){
31901         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
31902         if(node && this.childrenRendered){
31903             node.render();
31904         }
31905         this.ui.updateExpandIcon();
31906         return node;
31907     },
31908
31909     // private override
31910     removeChild : function(node){
31911         this.ownerTree.getSelectionModel().unselect(node);
31912         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
31913         // if it's been rendered remove dom node
31914         if(this.childrenRendered){
31915             node.ui.remove();
31916         }
31917         if(this.childNodes.length < 1){
31918             this.collapse(false, false);
31919         }else{
31920             this.ui.updateExpandIcon();
31921         }
31922         if(!this.firstChild) {
31923             this.childrenRendered = false;
31924         }
31925         return node;
31926     },
31927
31928     // private override
31929     insertBefore : function(node, refNode){
31930         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
31931         if(newNode && refNode && this.childrenRendered){
31932             node.render();
31933         }
31934         this.ui.updateExpandIcon();
31935         return newNode;
31936     },
31937
31938     /**
31939      * Sets the text for this node
31940      * @param {String} text
31941      */
31942     setText : function(text){
31943         var oldText = this.text;
31944         this.text = text;
31945         this.attributes.text = text;
31946         if(this.rendered){ // event without subscribing
31947             this.ui.onTextChange(this, text, oldText);
31948         }
31949         this.fireEvent("textchange", this, text, oldText);
31950     },
31951
31952     /**
31953      * Triggers selection of this node
31954      */
31955     select : function(){
31956         this.getOwnerTree().getSelectionModel().select(this);
31957     },
31958
31959     /**
31960      * Triggers deselection of this node
31961      */
31962     unselect : function(){
31963         this.getOwnerTree().getSelectionModel().unselect(this);
31964     },
31965
31966     /**
31967      * Returns true if this node is selected
31968      * @return {Boolean}
31969      */
31970     isSelected : function(){
31971         return this.getOwnerTree().getSelectionModel().isSelected(this);
31972     },
31973
31974     /**
31975      * Expand this node.
31976      * @param {Boolean} deep (optional) True to expand all children as well
31977      * @param {Boolean} anim (optional) false to cancel the default animation
31978      * @param {Function} callback (optional) A callback to be called when
31979      * expanding this node completes (does not wait for deep expand to complete).
31980      * Called with 1 parameter, this node.
31981      */
31982     expand : function(deep, anim, callback){
31983         if(!this.expanded){
31984             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
31985                 return;
31986             }
31987             if(!this.childrenRendered){
31988                 this.renderChildren();
31989             }
31990             this.expanded = true;
31991             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
31992                 this.ui.animExpand(function(){
31993                     this.fireEvent("expand", this);
31994                     if(typeof callback == "function"){
31995                         callback(this);
31996                     }
31997                     if(deep === true){
31998                         this.expandChildNodes(true);
31999                     }
32000                 }.createDelegate(this));
32001                 return;
32002             }else{
32003                 this.ui.expand();
32004                 this.fireEvent("expand", this);
32005                 if(typeof callback == "function"){
32006                     callback(this);
32007                 }
32008             }
32009         }else{
32010            if(typeof callback == "function"){
32011                callback(this);
32012            }
32013         }
32014         if(deep === true){
32015             this.expandChildNodes(true);
32016         }
32017     },
32018
32019     isHiddenRoot : function(){
32020         return this.isRoot && !this.getOwnerTree().rootVisible;
32021     },
32022
32023     /**
32024      * Collapse this node.
32025      * @param {Boolean} deep (optional) True to collapse all children as well
32026      * @param {Boolean} anim (optional) false to cancel the default animation
32027      */
32028     collapse : function(deep, anim){
32029         if(this.expanded && !this.isHiddenRoot()){
32030             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
32031                 return;
32032             }
32033             this.expanded = false;
32034             if((this.getOwnerTree().animate && anim !== false) || anim){
32035                 this.ui.animCollapse(function(){
32036                     this.fireEvent("collapse", this);
32037                     if(deep === true){
32038                         this.collapseChildNodes(true);
32039                     }
32040                 }.createDelegate(this));
32041                 return;
32042             }else{
32043                 this.ui.collapse();
32044                 this.fireEvent("collapse", this);
32045             }
32046         }
32047         if(deep === true){
32048             var cs = this.childNodes;
32049             for(var i = 0, len = cs.length; i < len; i++) {
32050                 cs[i].collapse(true, false);
32051             }
32052         }
32053     },
32054
32055     // private
32056     delayedExpand : function(delay){
32057         if(!this.expandProcId){
32058             this.expandProcId = this.expand.defer(delay, this);
32059         }
32060     },
32061
32062     // private
32063     cancelExpand : function(){
32064         if(this.expandProcId){
32065             clearTimeout(this.expandProcId);
32066         }
32067         this.expandProcId = false;
32068     },
32069
32070     /**
32071      * Toggles expanded/collapsed state of the node
32072      */
32073     toggle : function(){
32074         if(this.expanded){
32075             this.collapse();
32076         }else{
32077             this.expand();
32078         }
32079     },
32080
32081     /**
32082      * Ensures all parent nodes are expanded
32083      */
32084     ensureVisible : function(callback){
32085         var tree = this.getOwnerTree();
32086         tree.expandPath(this.parentNode.getPath(), false, function(){
32087             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
32088             Roo.callback(callback);
32089         }.createDelegate(this));
32090     },
32091
32092     /**
32093      * Expand all child nodes
32094      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
32095      */
32096     expandChildNodes : function(deep){
32097         var cs = this.childNodes;
32098         for(var i = 0, len = cs.length; i < len; i++) {
32099                 cs[i].expand(deep);
32100         }
32101     },
32102
32103     /**
32104      * Collapse all child nodes
32105      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
32106      */
32107     collapseChildNodes : function(deep){
32108         var cs = this.childNodes;
32109         for(var i = 0, len = cs.length; i < len; i++) {
32110                 cs[i].collapse(deep);
32111         }
32112     },
32113
32114     /**
32115      * Disables this node
32116      */
32117     disable : function(){
32118         this.disabled = true;
32119         this.unselect();
32120         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
32121             this.ui.onDisableChange(this, true);
32122         }
32123         this.fireEvent("disabledchange", this, true);
32124     },
32125
32126     /**
32127      * Enables this node
32128      */
32129     enable : function(){
32130         this.disabled = false;
32131         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
32132             this.ui.onDisableChange(this, false);
32133         }
32134         this.fireEvent("disabledchange", this, false);
32135     },
32136
32137     // private
32138     renderChildren : function(suppressEvent){
32139         if(suppressEvent !== false){
32140             this.fireEvent("beforechildrenrendered", this);
32141         }
32142         var cs = this.childNodes;
32143         for(var i = 0, len = cs.length; i < len; i++){
32144             cs[i].render(true);
32145         }
32146         this.childrenRendered = true;
32147     },
32148
32149     // private
32150     sort : function(fn, scope){
32151         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
32152         if(this.childrenRendered){
32153             var cs = this.childNodes;
32154             for(var i = 0, len = cs.length; i < len; i++){
32155                 cs[i].render(true);
32156             }
32157         }
32158     },
32159
32160     // private
32161     render : function(bulkRender){
32162         this.ui.render(bulkRender);
32163         if(!this.rendered){
32164             this.rendered = true;
32165             if(this.expanded){
32166                 this.expanded = false;
32167                 this.expand(false, false);
32168             }
32169         }
32170     },
32171
32172     // private
32173     renderIndent : function(deep, refresh){
32174         if(refresh){
32175             this.ui.childIndent = null;
32176         }
32177         this.ui.renderIndent();
32178         if(deep === true && this.childrenRendered){
32179             var cs = this.childNodes;
32180             for(var i = 0, len = cs.length; i < len; i++){
32181                 cs[i].renderIndent(true, refresh);
32182             }
32183         }
32184     }
32185 });/*
32186  * Based on:
32187  * Ext JS Library 1.1.1
32188  * Copyright(c) 2006-2007, Ext JS, LLC.
32189  *
32190  * Originally Released Under LGPL - original licence link has changed is not relivant.
32191  *
32192  * Fork - LGPL
32193  * <script type="text/javascript">
32194  */
32195  
32196 /**
32197  * @class Roo.tree.AsyncTreeNode
32198  * @extends Roo.tree.TreeNode
32199  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
32200  * @constructor
32201  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
32202  */
32203  Roo.tree.AsyncTreeNode = function(config){
32204     this.loaded = false;
32205     this.loading = false;
32206     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
32207     /**
32208     * @event beforeload
32209     * Fires before this node is loaded, return false to cancel
32210     * @param {Node} this This node
32211     */
32212     this.addEvents({'beforeload':true, 'load': true});
32213     /**
32214     * @event load
32215     * Fires when this node is loaded
32216     * @param {Node} this This node
32217     */
32218     /**
32219      * The loader used by this node (defaults to using the tree's defined loader)
32220      * @type TreeLoader
32221      * @property loader
32222      */
32223 };
32224 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
32225     expand : function(deep, anim, callback){
32226         if(this.loading){ // if an async load is already running, waiting til it's done
32227             var timer;
32228             var f = function(){
32229                 if(!this.loading){ // done loading
32230                     clearInterval(timer);
32231                     this.expand(deep, anim, callback);
32232                 }
32233             }.createDelegate(this);
32234             timer = setInterval(f, 200);
32235             return;
32236         }
32237         if(!this.loaded){
32238             if(this.fireEvent("beforeload", this) === false){
32239                 return;
32240             }
32241             this.loading = true;
32242             this.ui.beforeLoad(this);
32243             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
32244             if(loader){
32245                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
32246                 return;
32247             }
32248         }
32249         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
32250     },
32251     
32252     /**
32253      * Returns true if this node is currently loading
32254      * @return {Boolean}
32255      */
32256     isLoading : function(){
32257         return this.loading;  
32258     },
32259     
32260     loadComplete : function(deep, anim, callback){
32261         this.loading = false;
32262         this.loaded = true;
32263         this.ui.afterLoad(this);
32264         this.fireEvent("load", this);
32265         this.expand(deep, anim, callback);
32266     },
32267     
32268     /**
32269      * Returns true if this node has been loaded
32270      * @return {Boolean}
32271      */
32272     isLoaded : function(){
32273         return this.loaded;
32274     },
32275     
32276     hasChildNodes : function(){
32277         if(!this.isLeaf() && !this.loaded){
32278             return true;
32279         }else{
32280             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
32281         }
32282     },
32283
32284     /**
32285      * Trigger a reload for this node
32286      * @param {Function} callback
32287      */
32288     reload : function(callback){
32289         this.collapse(false, false);
32290         while(this.firstChild){
32291             this.removeChild(this.firstChild);
32292         }
32293         this.childrenRendered = false;
32294         this.loaded = false;
32295         if(this.isHiddenRoot()){
32296             this.expanded = false;
32297         }
32298         this.expand(false, false, callback);
32299     }
32300 });/*
32301  * Based on:
32302  * Ext JS Library 1.1.1
32303  * Copyright(c) 2006-2007, Ext JS, LLC.
32304  *
32305  * Originally Released Under LGPL - original licence link has changed is not relivant.
32306  *
32307  * Fork - LGPL
32308  * <script type="text/javascript">
32309  */
32310  
32311 /**
32312  * @class Roo.tree.TreeNodeUI
32313  * @constructor
32314  * @param {Object} node The node to render
32315  * The TreeNode UI implementation is separate from the
32316  * tree implementation. Unless you are customizing the tree UI,
32317  * you should never have to use this directly.
32318  */
32319 Roo.tree.TreeNodeUI = function(node){
32320     this.node = node;
32321     this.rendered = false;
32322     this.animating = false;
32323     this.emptyIcon = Roo.BLANK_IMAGE_URL;
32324 };
32325
32326 Roo.tree.TreeNodeUI.prototype = {
32327     removeChild : function(node){
32328         if(this.rendered){
32329             this.ctNode.removeChild(node.ui.getEl());
32330         }
32331     },
32332
32333     beforeLoad : function(){
32334          this.addClass("x-tree-node-loading");
32335     },
32336
32337     afterLoad : function(){
32338          this.removeClass("x-tree-node-loading");
32339     },
32340
32341     onTextChange : function(node, text, oldText){
32342         if(this.rendered){
32343             this.textNode.innerHTML = text;
32344         }
32345     },
32346
32347     onDisableChange : function(node, state){
32348         this.disabled = state;
32349         if(state){
32350             this.addClass("x-tree-node-disabled");
32351         }else{
32352             this.removeClass("x-tree-node-disabled");
32353         }
32354     },
32355
32356     onSelectedChange : function(state){
32357         if(state){
32358             this.focus();
32359             this.addClass("x-tree-selected");
32360         }else{
32361             //this.blur();
32362             this.removeClass("x-tree-selected");
32363         }
32364     },
32365
32366     onMove : function(tree, node, oldParent, newParent, index, refNode){
32367         this.childIndent = null;
32368         if(this.rendered){
32369             var targetNode = newParent.ui.getContainer();
32370             if(!targetNode){//target not rendered
32371                 this.holder = document.createElement("div");
32372                 this.holder.appendChild(this.wrap);
32373                 return;
32374             }
32375             var insertBefore = refNode ? refNode.ui.getEl() : null;
32376             if(insertBefore){
32377                 targetNode.insertBefore(this.wrap, insertBefore);
32378             }else{
32379                 targetNode.appendChild(this.wrap);
32380             }
32381             this.node.renderIndent(true);
32382         }
32383     },
32384
32385     addClass : function(cls){
32386         if(this.elNode){
32387             Roo.fly(this.elNode).addClass(cls);
32388         }
32389     },
32390
32391     removeClass : function(cls){
32392         if(this.elNode){
32393             Roo.fly(this.elNode).removeClass(cls);
32394         }
32395     },
32396
32397     remove : function(){
32398         if(this.rendered){
32399             this.holder = document.createElement("div");
32400             this.holder.appendChild(this.wrap);
32401         }
32402     },
32403
32404     fireEvent : function(){
32405         return this.node.fireEvent.apply(this.node, arguments);
32406     },
32407
32408     initEvents : function(){
32409         this.node.on("move", this.onMove, this);
32410         var E = Roo.EventManager;
32411         var a = this.anchor;
32412
32413         var el = Roo.fly(a, '_treeui');
32414
32415         if(Roo.isOpera){ // opera render bug ignores the CSS
32416             el.setStyle("text-decoration", "none");
32417         }
32418
32419         el.on("click", this.onClick, this);
32420         el.on("dblclick", this.onDblClick, this);
32421
32422         if(this.checkbox){
32423             Roo.EventManager.on(this.checkbox,
32424                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
32425         }
32426
32427         el.on("contextmenu", this.onContextMenu, this);
32428
32429         var icon = Roo.fly(this.iconNode);
32430         icon.on("click", this.onClick, this);
32431         icon.on("dblclick", this.onDblClick, this);
32432         icon.on("contextmenu", this.onContextMenu, this);
32433         E.on(this.ecNode, "click", this.ecClick, this, true);
32434
32435         if(this.node.disabled){
32436             this.addClass("x-tree-node-disabled");
32437         }
32438         if(this.node.hidden){
32439             this.addClass("x-tree-node-disabled");
32440         }
32441         var ot = this.node.getOwnerTree();
32442         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
32443         if(dd && (!this.node.isRoot || ot.rootVisible)){
32444             Roo.dd.Registry.register(this.elNode, {
32445                 node: this.node,
32446                 handles: this.getDDHandles(),
32447                 isHandle: false
32448             });
32449         }
32450     },
32451
32452     getDDHandles : function(){
32453         return [this.iconNode, this.textNode];
32454     },
32455
32456     hide : function(){
32457         if(this.rendered){
32458             this.wrap.style.display = "none";
32459         }
32460     },
32461
32462     show : function(){
32463         if(this.rendered){
32464             this.wrap.style.display = "";
32465         }
32466     },
32467
32468     onContextMenu : function(e){
32469         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
32470             e.preventDefault();
32471             this.focus();
32472             this.fireEvent("contextmenu", this.node, e);
32473         }
32474     },
32475
32476     onClick : function(e){
32477         if(this.dropping){
32478             e.stopEvent();
32479             return;
32480         }
32481         if(this.fireEvent("beforeclick", this.node, e) !== false){
32482             if(!this.disabled && this.node.attributes.href){
32483                 this.fireEvent("click", this.node, e);
32484                 return;
32485             }
32486             e.preventDefault();
32487             if(this.disabled){
32488                 return;
32489             }
32490
32491             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
32492                 this.node.toggle();
32493             }
32494
32495             this.fireEvent("click", this.node, e);
32496         }else{
32497             e.stopEvent();
32498         }
32499     },
32500
32501     onDblClick : function(e){
32502         e.preventDefault();
32503         if(this.disabled){
32504             return;
32505         }
32506         if(this.checkbox){
32507             this.toggleCheck();
32508         }
32509         if(!this.animating && this.node.hasChildNodes()){
32510             this.node.toggle();
32511         }
32512         this.fireEvent("dblclick", this.node, e);
32513     },
32514
32515     onCheckChange : function(){
32516         var checked = this.checkbox.checked;
32517         this.node.attributes.checked = checked;
32518         this.fireEvent('checkchange', this.node, checked);
32519     },
32520
32521     ecClick : function(e){
32522         if(!this.animating && this.node.hasChildNodes()){
32523             this.node.toggle();
32524         }
32525     },
32526
32527     startDrop : function(){
32528         this.dropping = true;
32529     },
32530
32531     // delayed drop so the click event doesn't get fired on a drop
32532     endDrop : function(){
32533        setTimeout(function(){
32534            this.dropping = false;
32535        }.createDelegate(this), 50);
32536     },
32537
32538     expand : function(){
32539         this.updateExpandIcon();
32540         this.ctNode.style.display = "";
32541     },
32542
32543     focus : function(){
32544         if(!this.node.preventHScroll){
32545             try{this.anchor.focus();
32546             }catch(e){}
32547         }else if(!Roo.isIE){
32548             try{
32549                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
32550                 var l = noscroll.scrollLeft;
32551                 this.anchor.focus();
32552                 noscroll.scrollLeft = l;
32553             }catch(e){}
32554         }
32555     },
32556
32557     toggleCheck : function(value){
32558         var cb = this.checkbox;
32559         if(cb){
32560             cb.checked = (value === undefined ? !cb.checked : value);
32561         }
32562     },
32563
32564     blur : function(){
32565         try{
32566             this.anchor.blur();
32567         }catch(e){}
32568     },
32569
32570     animExpand : function(callback){
32571         var ct = Roo.get(this.ctNode);
32572         ct.stopFx();
32573         if(!this.node.hasChildNodes()){
32574             this.updateExpandIcon();
32575             this.ctNode.style.display = "";
32576             Roo.callback(callback);
32577             return;
32578         }
32579         this.animating = true;
32580         this.updateExpandIcon();
32581
32582         ct.slideIn('t', {
32583            callback : function(){
32584                this.animating = false;
32585                Roo.callback(callback);
32586             },
32587             scope: this,
32588             duration: this.node.ownerTree.duration || .25
32589         });
32590     },
32591
32592     highlight : function(){
32593         var tree = this.node.getOwnerTree();
32594         Roo.fly(this.wrap).highlight(
32595             tree.hlColor || "C3DAF9",
32596             {endColor: tree.hlBaseColor}
32597         );
32598     },
32599
32600     collapse : function(){
32601         this.updateExpandIcon();
32602         this.ctNode.style.display = "none";
32603     },
32604
32605     animCollapse : function(callback){
32606         var ct = Roo.get(this.ctNode);
32607         ct.enableDisplayMode('block');
32608         ct.stopFx();
32609
32610         this.animating = true;
32611         this.updateExpandIcon();
32612
32613         ct.slideOut('t', {
32614             callback : function(){
32615                this.animating = false;
32616                Roo.callback(callback);
32617             },
32618             scope: this,
32619             duration: this.node.ownerTree.duration || .25
32620         });
32621     },
32622
32623     getContainer : function(){
32624         return this.ctNode;
32625     },
32626
32627     getEl : function(){
32628         return this.wrap;
32629     },
32630
32631     appendDDGhost : function(ghostNode){
32632         ghostNode.appendChild(this.elNode.cloneNode(true));
32633     },
32634
32635     getDDRepairXY : function(){
32636         return Roo.lib.Dom.getXY(this.iconNode);
32637     },
32638
32639     onRender : function(){
32640         this.render();
32641     },
32642
32643     render : function(bulkRender){
32644         var n = this.node, a = n.attributes;
32645         var targetNode = n.parentNode ?
32646               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
32647
32648         if(!this.rendered){
32649             this.rendered = true;
32650
32651             this.renderElements(n, a, targetNode, bulkRender);
32652
32653             if(a.qtip){
32654                if(this.textNode.setAttributeNS){
32655                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
32656                    if(a.qtipTitle){
32657                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
32658                    }
32659                }else{
32660                    this.textNode.setAttribute("ext:qtip", a.qtip);
32661                    if(a.qtipTitle){
32662                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
32663                    }
32664                }
32665             }else if(a.qtipCfg){
32666                 a.qtipCfg.target = Roo.id(this.textNode);
32667                 Roo.QuickTips.register(a.qtipCfg);
32668             }
32669             this.initEvents();
32670             if(!this.node.expanded){
32671                 this.updateExpandIcon();
32672             }
32673         }else{
32674             if(bulkRender === true) {
32675                 targetNode.appendChild(this.wrap);
32676             }
32677         }
32678     },
32679
32680     renderElements : function(n, a, targetNode, bulkRender)
32681     {
32682         // add some indent caching, this helps performance when rendering a large tree
32683         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
32684         var t = n.getOwnerTree();
32685         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
32686         if (typeof(n.attributes.html) != 'undefined') {
32687             txt = n.attributes.html;
32688         }
32689         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
32690         var cb = typeof a.checked == 'boolean';
32691         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
32692         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
32693             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
32694             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
32695             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
32696             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
32697             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
32698              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
32699                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
32700             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
32701             "</li>"];
32702
32703         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
32704             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
32705                                 n.nextSibling.ui.getEl(), buf.join(""));
32706         }else{
32707             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
32708         }
32709
32710         this.elNode = this.wrap.childNodes[0];
32711         this.ctNode = this.wrap.childNodes[1];
32712         var cs = this.elNode.childNodes;
32713         this.indentNode = cs[0];
32714         this.ecNode = cs[1];
32715         this.iconNode = cs[2];
32716         var index = 3;
32717         if(cb){
32718             this.checkbox = cs[3];
32719             index++;
32720         }
32721         this.anchor = cs[index];
32722         this.textNode = cs[index].firstChild;
32723     },
32724
32725     getAnchor : function(){
32726         return this.anchor;
32727     },
32728
32729     getTextEl : function(){
32730         return this.textNode;
32731     },
32732
32733     getIconEl : function(){
32734         return this.iconNode;
32735     },
32736
32737     isChecked : function(){
32738         return this.checkbox ? this.checkbox.checked : false;
32739     },
32740
32741     updateExpandIcon : function(){
32742         if(this.rendered){
32743             var n = this.node, c1, c2;
32744             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
32745             var hasChild = n.hasChildNodes();
32746             if(hasChild){
32747                 if(n.expanded){
32748                     cls += "-minus";
32749                     c1 = "x-tree-node-collapsed";
32750                     c2 = "x-tree-node-expanded";
32751                 }else{
32752                     cls += "-plus";
32753                     c1 = "x-tree-node-expanded";
32754                     c2 = "x-tree-node-collapsed";
32755                 }
32756                 if(this.wasLeaf){
32757                     this.removeClass("x-tree-node-leaf");
32758                     this.wasLeaf = false;
32759                 }
32760                 if(this.c1 != c1 || this.c2 != c2){
32761                     Roo.fly(this.elNode).replaceClass(c1, c2);
32762                     this.c1 = c1; this.c2 = c2;
32763                 }
32764             }else{
32765                 // this changes non-leafs into leafs if they have no children.
32766                 // it's not very rational behaviour..
32767                 
32768                 if(!this.wasLeaf && this.node.leaf){
32769                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
32770                     delete this.c1;
32771                     delete this.c2;
32772                     this.wasLeaf = true;
32773                 }
32774             }
32775             var ecc = "x-tree-ec-icon "+cls;
32776             if(this.ecc != ecc){
32777                 this.ecNode.className = ecc;
32778                 this.ecc = ecc;
32779             }
32780         }
32781     },
32782
32783     getChildIndent : function(){
32784         if(!this.childIndent){
32785             var buf = [];
32786             var p = this.node;
32787             while(p){
32788                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
32789                     if(!p.isLast()) {
32790                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
32791                     } else {
32792                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
32793                     }
32794                 }
32795                 p = p.parentNode;
32796             }
32797             this.childIndent = buf.join("");
32798         }
32799         return this.childIndent;
32800     },
32801
32802     renderIndent : function(){
32803         if(this.rendered){
32804             var indent = "";
32805             var p = this.node.parentNode;
32806             if(p){
32807                 indent = p.ui.getChildIndent();
32808             }
32809             if(this.indentMarkup != indent){ // don't rerender if not required
32810                 this.indentNode.innerHTML = indent;
32811                 this.indentMarkup = indent;
32812             }
32813             this.updateExpandIcon();
32814         }
32815     }
32816 };
32817
32818 Roo.tree.RootTreeNodeUI = function(){
32819     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
32820 };
32821 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
32822     render : function(){
32823         if(!this.rendered){
32824             var targetNode = this.node.ownerTree.innerCt.dom;
32825             this.node.expanded = true;
32826             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
32827             this.wrap = this.ctNode = targetNode.firstChild;
32828         }
32829     },
32830     collapse : function(){
32831     },
32832     expand : function(){
32833     }
32834 });/*
32835  * Based on:
32836  * Ext JS Library 1.1.1
32837  * Copyright(c) 2006-2007, Ext JS, LLC.
32838  *
32839  * Originally Released Under LGPL - original licence link has changed is not relivant.
32840  *
32841  * Fork - LGPL
32842  * <script type="text/javascript">
32843  */
32844 /**
32845  * @class Roo.tree.TreeLoader
32846  * @extends Roo.util.Observable
32847  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
32848  * nodes from a specified URL. The response must be a javascript Array definition
32849  * who's elements are node definition objects. eg:
32850  * <pre><code>
32851    [{ 'id': 1, 'text': 'A folder Node', 'leaf': false },
32852     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }]
32853 </code></pre>
32854  * <br><br>
32855  * A server request is sent, and child nodes are loaded only when a node is expanded.
32856  * The loading node's id is passed to the server under the parameter name "node" to
32857  * enable the server to produce the correct child nodes.
32858  * <br><br>
32859  * To pass extra parameters, an event handler may be attached to the "beforeload"
32860  * event, and the parameters specified in the TreeLoader's baseParams property:
32861  * <pre><code>
32862     myTreeLoader.on("beforeload", function(treeLoader, node) {
32863         this.baseParams.category = node.attributes.category;
32864     }, this);
32865 </code></pre><
32866  * This would pass an HTTP parameter called "category" to the server containing
32867  * the value of the Node's "category" attribute.
32868  * @constructor
32869  * Creates a new Treeloader.
32870  * @param {Object} config A config object containing config properties.
32871  */
32872 Roo.tree.TreeLoader = function(config){
32873     this.baseParams = {};
32874     this.requestMethod = "POST";
32875     Roo.apply(this, config);
32876
32877     this.addEvents({
32878     
32879         /**
32880          * @event beforeload
32881          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
32882          * @param {Object} This TreeLoader object.
32883          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32884          * @param {Object} callback The callback function specified in the {@link #load} call.
32885          */
32886         beforeload : true,
32887         /**
32888          * @event load
32889          * Fires when the node has been successfuly loaded.
32890          * @param {Object} This TreeLoader object.
32891          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32892          * @param {Object} response The response object containing the data from the server.
32893          */
32894         load : true,
32895         /**
32896          * @event loadexception
32897          * Fires if the network request failed.
32898          * @param {Object} This TreeLoader object.
32899          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32900          * @param {Object} response The response object containing the data from the server.
32901          */
32902         loadexception : true,
32903         /**
32904          * @event create
32905          * Fires before a node is created, enabling you to return custom Node types 
32906          * @param {Object} This TreeLoader object.
32907          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
32908          */
32909         create : true
32910     });
32911
32912     Roo.tree.TreeLoader.superclass.constructor.call(this);
32913 };
32914
32915 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
32916     /**
32917     * @cfg {String} dataUrl The URL from which to request a Json string which
32918     * specifies an array of node definition object representing the child nodes
32919     * to be loaded.
32920     */
32921     /**
32922     * @cfg {Object} baseParams (optional) An object containing properties which
32923     * specify HTTP parameters to be passed to each request for child nodes.
32924     */
32925     /**
32926     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
32927     * created by this loader. If the attributes sent by the server have an attribute in this object,
32928     * they take priority.
32929     */
32930     /**
32931     * @cfg {Object} uiProviders (optional) An object containing properties which
32932     * 
32933     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
32934     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
32935     * <i>uiProvider</i> attribute of a returned child node is a string rather
32936     * than a reference to a TreeNodeUI implementation, this that string value
32937     * is used as a property name in the uiProviders object. You can define the provider named
32938     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
32939     */
32940     uiProviders : {},
32941
32942     /**
32943     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
32944     * child nodes before loading.
32945     */
32946     clearOnLoad : true,
32947
32948     /**
32949     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
32950     * property on loading, rather than expecting an array. (eg. more compatible to a standard
32951     * Grid query { data : [ .....] }
32952     */
32953     
32954     root : false,
32955      /**
32956     * @cfg {String} queryParam (optional) 
32957     * Name of the query as it will be passed on the querystring (defaults to 'node')
32958     * eg. the request will be ?node=[id]
32959     */
32960     
32961     
32962     queryParam: false,
32963     
32964     /**
32965      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
32966      * This is called automatically when a node is expanded, but may be used to reload
32967      * a node (or append new children if the {@link #clearOnLoad} option is false.)
32968      * @param {Roo.tree.TreeNode} node
32969      * @param {Function} callback
32970      */
32971     load : function(node, callback){
32972         if(this.clearOnLoad){
32973             while(node.firstChild){
32974                 node.removeChild(node.firstChild);
32975             }
32976         }
32977         if(node.attributes.children){ // preloaded json children
32978             var cs = node.attributes.children;
32979             for(var i = 0, len = cs.length; i < len; i++){
32980                 node.appendChild(this.createNode(cs[i]));
32981             }
32982             if(typeof callback == "function"){
32983                 callback();
32984             }
32985         }else if(this.dataUrl){
32986             this.requestData(node, callback);
32987         }
32988     },
32989
32990     getParams: function(node){
32991         var buf = [], bp = this.baseParams;
32992         for(var key in bp){
32993             if(typeof bp[key] != "function"){
32994                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
32995             }
32996         }
32997         var n = this.queryParam === false ? 'node' : this.queryParam;
32998         buf.push(n + "=", encodeURIComponent(node.id));
32999         return buf.join("");
33000     },
33001
33002     requestData : function(node, callback){
33003         if(this.fireEvent("beforeload", this, node, callback) !== false){
33004             this.transId = Roo.Ajax.request({
33005                 method:this.requestMethod,
33006                 url: this.dataUrl||this.url,
33007                 success: this.handleResponse,
33008                 failure: this.handleFailure,
33009                 scope: this,
33010                 argument: {callback: callback, node: node},
33011                 params: this.getParams(node)
33012             });
33013         }else{
33014             // if the load is cancelled, make sure we notify
33015             // the node that we are done
33016             if(typeof callback == "function"){
33017                 callback();
33018             }
33019         }
33020     },
33021
33022     isLoading : function(){
33023         return this.transId ? true : false;
33024     },
33025
33026     abort : function(){
33027         if(this.isLoading()){
33028             Roo.Ajax.abort(this.transId);
33029         }
33030     },
33031
33032     // private
33033     createNode : function(attr)
33034     {
33035         // apply baseAttrs, nice idea Corey!
33036         if(this.baseAttrs){
33037             Roo.applyIf(attr, this.baseAttrs);
33038         }
33039         if(this.applyLoader !== false){
33040             attr.loader = this;
33041         }
33042         // uiProvider = depreciated..
33043         
33044         if(typeof(attr.uiProvider) == 'string'){
33045            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
33046                 /**  eval:var:attr */ eval(attr.uiProvider);
33047         }
33048         if(typeof(this.uiProviders['default']) != 'undefined') {
33049             attr.uiProvider = this.uiProviders['default'];
33050         }
33051         
33052         this.fireEvent('create', this, attr);
33053         
33054         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
33055         return(attr.leaf ?
33056                         new Roo.tree.TreeNode(attr) :
33057                         new Roo.tree.AsyncTreeNode(attr));
33058     },
33059
33060     processResponse : function(response, node, callback)
33061     {
33062         var json = response.responseText;
33063         try {
33064             
33065             var o = Roo.decode(json);
33066             
33067             if (!o.success) {
33068                 // it's a failure condition.
33069                 var a = response.argument;
33070                 this.fireEvent("loadexception", this, a.node, response);
33071                 Roo.log("Load failed - should have a handler really");
33072                 return;
33073             }
33074             
33075             if (this.root !== false) {
33076                 o = o[this.root];
33077             }
33078             
33079             for(var i = 0, len = o.length; i < len; i++){
33080                 var n = this.createNode(o[i]);
33081                 if(n){
33082                     node.appendChild(n);
33083                 }
33084             }
33085             if(typeof callback == "function"){
33086                 callback(this, node);
33087             }
33088         }catch(e){
33089             this.handleFailure(response);
33090         }
33091     },
33092
33093     handleResponse : function(response){
33094         this.transId = false;
33095         var a = response.argument;
33096         this.processResponse(response, a.node, a.callback);
33097         this.fireEvent("load", this, a.node, response);
33098     },
33099
33100     handleFailure : function(response)
33101     {
33102         // should handle failure better..
33103         this.transId = false;
33104         var a = response.argument;
33105         this.fireEvent("loadexception", this, a.node, response);
33106         if(typeof a.callback == "function"){
33107             a.callback(this, a.node);
33108         }
33109     }
33110 });/*
33111  * Based on:
33112  * Ext JS Library 1.1.1
33113  * Copyright(c) 2006-2007, Ext JS, LLC.
33114  *
33115  * Originally Released Under LGPL - original licence link has changed is not relivant.
33116  *
33117  * Fork - LGPL
33118  * <script type="text/javascript">
33119  */
33120
33121 /**
33122 * @class Roo.tree.TreeFilter
33123 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
33124 * @param {TreePanel} tree
33125 * @param {Object} config (optional)
33126  */
33127 Roo.tree.TreeFilter = function(tree, config){
33128     this.tree = tree;
33129     this.filtered = {};
33130     Roo.apply(this, config);
33131 };
33132
33133 Roo.tree.TreeFilter.prototype = {
33134     clearBlank:false,
33135     reverse:false,
33136     autoClear:false,
33137     remove:false,
33138
33139      /**
33140      * Filter the data by a specific attribute.
33141      * @param {String/RegExp} value Either string that the attribute value
33142      * should start with or a RegExp to test against the attribute
33143      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
33144      * @param {TreeNode} startNode (optional) The node to start the filter at.
33145      */
33146     filter : function(value, attr, startNode){
33147         attr = attr || "text";
33148         var f;
33149         if(typeof value == "string"){
33150             var vlen = value.length;
33151             // auto clear empty filter
33152             if(vlen == 0 && this.clearBlank){
33153                 this.clear();
33154                 return;
33155             }
33156             value = value.toLowerCase();
33157             f = function(n){
33158                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
33159             };
33160         }else if(value.exec){ // regex?
33161             f = function(n){
33162                 return value.test(n.attributes[attr]);
33163             };
33164         }else{
33165             throw 'Illegal filter type, must be string or regex';
33166         }
33167         this.filterBy(f, null, startNode);
33168         },
33169
33170     /**
33171      * Filter by a function. The passed function will be called with each
33172      * node in the tree (or from the startNode). If the function returns true, the node is kept
33173      * otherwise it is filtered. If a node is filtered, its children are also filtered.
33174      * @param {Function} fn The filter function
33175      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
33176      */
33177     filterBy : function(fn, scope, startNode){
33178         startNode = startNode || this.tree.root;
33179         if(this.autoClear){
33180             this.clear();
33181         }
33182         var af = this.filtered, rv = this.reverse;
33183         var f = function(n){
33184             if(n == startNode){
33185                 return true;
33186             }
33187             if(af[n.id]){
33188                 return false;
33189             }
33190             var m = fn.call(scope || n, n);
33191             if(!m || rv){
33192                 af[n.id] = n;
33193                 n.ui.hide();
33194                 return false;
33195             }
33196             return true;
33197         };
33198         startNode.cascade(f);
33199         if(this.remove){
33200            for(var id in af){
33201                if(typeof id != "function"){
33202                    var n = af[id];
33203                    if(n && n.parentNode){
33204                        n.parentNode.removeChild(n);
33205                    }
33206                }
33207            }
33208         }
33209     },
33210
33211     /**
33212      * Clears the current filter. Note: with the "remove" option
33213      * set a filter cannot be cleared.
33214      */
33215     clear : function(){
33216         var t = this.tree;
33217         var af = this.filtered;
33218         for(var id in af){
33219             if(typeof id != "function"){
33220                 var n = af[id];
33221                 if(n){
33222                     n.ui.show();
33223                 }
33224             }
33225         }
33226         this.filtered = {};
33227     }
33228 };
33229 /*
33230  * Based on:
33231  * Ext JS Library 1.1.1
33232  * Copyright(c) 2006-2007, Ext JS, LLC.
33233  *
33234  * Originally Released Under LGPL - original licence link has changed is not relivant.
33235  *
33236  * Fork - LGPL
33237  * <script type="text/javascript">
33238  */
33239  
33240
33241 /**
33242  * @class Roo.tree.TreeSorter
33243  * Provides sorting of nodes in a TreePanel
33244  * 
33245  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
33246  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
33247  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
33248  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
33249  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
33250  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
33251  * @constructor
33252  * @param {TreePanel} tree
33253  * @param {Object} config
33254  */
33255 Roo.tree.TreeSorter = function(tree, config){
33256     Roo.apply(this, config);
33257     tree.on("beforechildrenrendered", this.doSort, this);
33258     tree.on("append", this.updateSort, this);
33259     tree.on("insert", this.updateSort, this);
33260     
33261     var dsc = this.dir && this.dir.toLowerCase() == "desc";
33262     var p = this.property || "text";
33263     var sortType = this.sortType;
33264     var fs = this.folderSort;
33265     var cs = this.caseSensitive === true;
33266     var leafAttr = this.leafAttr || 'leaf';
33267
33268     this.sortFn = function(n1, n2){
33269         if(fs){
33270             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
33271                 return 1;
33272             }
33273             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
33274                 return -1;
33275             }
33276         }
33277         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
33278         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
33279         if(v1 < v2){
33280                         return dsc ? +1 : -1;
33281                 }else if(v1 > v2){
33282                         return dsc ? -1 : +1;
33283         }else{
33284                 return 0;
33285         }
33286     };
33287 };
33288
33289 Roo.tree.TreeSorter.prototype = {
33290     doSort : function(node){
33291         node.sort(this.sortFn);
33292     },
33293     
33294     compareNodes : function(n1, n2){
33295         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
33296     },
33297     
33298     updateSort : function(tree, node){
33299         if(node.childrenRendered){
33300             this.doSort.defer(1, this, [node]);
33301         }
33302     }
33303 };/*
33304  * Based on:
33305  * Ext JS Library 1.1.1
33306  * Copyright(c) 2006-2007, Ext JS, LLC.
33307  *
33308  * Originally Released Under LGPL - original licence link has changed is not relivant.
33309  *
33310  * Fork - LGPL
33311  * <script type="text/javascript">
33312  */
33313
33314 if(Roo.dd.DropZone){
33315     
33316 Roo.tree.TreeDropZone = function(tree, config){
33317     this.allowParentInsert = false;
33318     this.allowContainerDrop = false;
33319     this.appendOnly = false;
33320     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
33321     this.tree = tree;
33322     this.lastInsertClass = "x-tree-no-status";
33323     this.dragOverData = {};
33324 };
33325
33326 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
33327     ddGroup : "TreeDD",
33328     
33329     expandDelay : 1000,
33330     
33331     expandNode : function(node){
33332         if(node.hasChildNodes() && !node.isExpanded()){
33333             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
33334         }
33335     },
33336     
33337     queueExpand : function(node){
33338         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
33339     },
33340     
33341     cancelExpand : function(){
33342         if(this.expandProcId){
33343             clearTimeout(this.expandProcId);
33344             this.expandProcId = false;
33345         }
33346     },
33347     
33348     isValidDropPoint : function(n, pt, dd, e, data){
33349         if(!n || !data){ return false; }
33350         var targetNode = n.node;
33351         var dropNode = data.node;
33352         // default drop rules
33353         if(!(targetNode && targetNode.isTarget && pt)){
33354             return false;
33355         }
33356         if(pt == "append" && targetNode.allowChildren === false){
33357             return false;
33358         }
33359         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
33360             return false;
33361         }
33362         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
33363             return false;
33364         }
33365         // reuse the object
33366         var overEvent = this.dragOverData;
33367         overEvent.tree = this.tree;
33368         overEvent.target = targetNode;
33369         overEvent.data = data;
33370         overEvent.point = pt;
33371         overEvent.source = dd;
33372         overEvent.rawEvent = e;
33373         overEvent.dropNode = dropNode;
33374         overEvent.cancel = false;  
33375         var result = this.tree.fireEvent("nodedragover", overEvent);
33376         return overEvent.cancel === false && result !== false;
33377     },
33378     
33379     getDropPoint : function(e, n, dd){
33380         var tn = n.node;
33381         if(tn.isRoot){
33382             return tn.allowChildren !== false ? "append" : false; // always append for root
33383         }
33384         var dragEl = n.ddel;
33385         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
33386         var y = Roo.lib.Event.getPageY(e);
33387         //var noAppend = tn.allowChildren === false || tn.isLeaf();
33388         
33389         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
33390         var noAppend = tn.allowChildren === false;
33391         if(this.appendOnly || tn.parentNode.allowChildren === false){
33392             return noAppend ? false : "append";
33393         }
33394         var noBelow = false;
33395         if(!this.allowParentInsert){
33396             noBelow = tn.hasChildNodes() && tn.isExpanded();
33397         }
33398         var q = (b - t) / (noAppend ? 2 : 3);
33399         if(y >= t && y < (t + q)){
33400             return "above";
33401         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
33402             return "below";
33403         }else{
33404             return "append";
33405         }
33406     },
33407     
33408     onNodeEnter : function(n, dd, e, data){
33409         this.cancelExpand();
33410     },
33411     
33412     onNodeOver : function(n, dd, e, data){
33413         var pt = this.getDropPoint(e, n, dd);
33414         var node = n.node;
33415         
33416         // auto node expand check
33417         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
33418             this.queueExpand(node);
33419         }else if(pt != "append"){
33420             this.cancelExpand();
33421         }
33422         
33423         // set the insert point style on the target node
33424         var returnCls = this.dropNotAllowed;
33425         if(this.isValidDropPoint(n, pt, dd, e, data)){
33426            if(pt){
33427                var el = n.ddel;
33428                var cls;
33429                if(pt == "above"){
33430                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
33431                    cls = "x-tree-drag-insert-above";
33432                }else if(pt == "below"){
33433                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
33434                    cls = "x-tree-drag-insert-below";
33435                }else{
33436                    returnCls = "x-tree-drop-ok-append";
33437                    cls = "x-tree-drag-append";
33438                }
33439                if(this.lastInsertClass != cls){
33440                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
33441                    this.lastInsertClass = cls;
33442                }
33443            }
33444        }
33445        return returnCls;
33446     },
33447     
33448     onNodeOut : function(n, dd, e, data){
33449         this.cancelExpand();
33450         this.removeDropIndicators(n);
33451     },
33452     
33453     onNodeDrop : function(n, dd, e, data){
33454         var point = this.getDropPoint(e, n, dd);
33455         var targetNode = n.node;
33456         targetNode.ui.startDrop();
33457         if(!this.isValidDropPoint(n, point, dd, e, data)){
33458             targetNode.ui.endDrop();
33459             return false;
33460         }
33461         // first try to find the drop node
33462         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
33463         var dropEvent = {
33464             tree : this.tree,
33465             target: targetNode,
33466             data: data,
33467             point: point,
33468             source: dd,
33469             rawEvent: e,
33470             dropNode: dropNode,
33471             cancel: !dropNode   
33472         };
33473         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
33474         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
33475             targetNode.ui.endDrop();
33476             return false;
33477         }
33478         // allow target changing
33479         targetNode = dropEvent.target;
33480         if(point == "append" && !targetNode.isExpanded()){
33481             targetNode.expand(false, null, function(){
33482                 this.completeDrop(dropEvent);
33483             }.createDelegate(this));
33484         }else{
33485             this.completeDrop(dropEvent);
33486         }
33487         return true;
33488     },
33489     
33490     completeDrop : function(de){
33491         var ns = de.dropNode, p = de.point, t = de.target;
33492         if(!(ns instanceof Array)){
33493             ns = [ns];
33494         }
33495         var n;
33496         for(var i = 0, len = ns.length; i < len; i++){
33497             n = ns[i];
33498             if(p == "above"){
33499                 t.parentNode.insertBefore(n, t);
33500             }else if(p == "below"){
33501                 t.parentNode.insertBefore(n, t.nextSibling);
33502             }else{
33503                 t.appendChild(n);
33504             }
33505         }
33506         n.ui.focus();
33507         if(this.tree.hlDrop){
33508             n.ui.highlight();
33509         }
33510         t.ui.endDrop();
33511         this.tree.fireEvent("nodedrop", de);
33512     },
33513     
33514     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
33515         if(this.tree.hlDrop){
33516             dropNode.ui.focus();
33517             dropNode.ui.highlight();
33518         }
33519         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
33520     },
33521     
33522     getTree : function(){
33523         return this.tree;
33524     },
33525     
33526     removeDropIndicators : function(n){
33527         if(n && n.ddel){
33528             var el = n.ddel;
33529             Roo.fly(el).removeClass([
33530                     "x-tree-drag-insert-above",
33531                     "x-tree-drag-insert-below",
33532                     "x-tree-drag-append"]);
33533             this.lastInsertClass = "_noclass";
33534         }
33535     },
33536     
33537     beforeDragDrop : function(target, e, id){
33538         this.cancelExpand();
33539         return true;
33540     },
33541     
33542     afterRepair : function(data){
33543         if(data && Roo.enableFx){
33544             data.node.ui.highlight();
33545         }
33546         this.hideProxy();
33547     }    
33548 });
33549
33550 }
33551 /*
33552  * Based on:
33553  * Ext JS Library 1.1.1
33554  * Copyright(c) 2006-2007, Ext JS, LLC.
33555  *
33556  * Originally Released Under LGPL - original licence link has changed is not relivant.
33557  *
33558  * Fork - LGPL
33559  * <script type="text/javascript">
33560  */
33561  
33562
33563 if(Roo.dd.DragZone){
33564 Roo.tree.TreeDragZone = function(tree, config){
33565     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
33566     this.tree = tree;
33567 };
33568
33569 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
33570     ddGroup : "TreeDD",
33571     
33572     onBeforeDrag : function(data, e){
33573         var n = data.node;
33574         return n && n.draggable && !n.disabled;
33575     },
33576     
33577     onInitDrag : function(e){
33578         var data = this.dragData;
33579         this.tree.getSelectionModel().select(data.node);
33580         this.proxy.update("");
33581         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
33582         this.tree.fireEvent("startdrag", this.tree, data.node, e);
33583     },
33584     
33585     getRepairXY : function(e, data){
33586         return data.node.ui.getDDRepairXY();
33587     },
33588     
33589     onEndDrag : function(data, e){
33590         this.tree.fireEvent("enddrag", this.tree, data.node, e);
33591     },
33592     
33593     onValidDrop : function(dd, e, id){
33594         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
33595         this.hideProxy();
33596     },
33597     
33598     beforeInvalidDrop : function(e, id){
33599         // this scrolls the original position back into view
33600         var sm = this.tree.getSelectionModel();
33601         sm.clearSelections();
33602         sm.select(this.dragData.node);
33603     }
33604 });
33605 }/*
33606  * Based on:
33607  * Ext JS Library 1.1.1
33608  * Copyright(c) 2006-2007, Ext JS, LLC.
33609  *
33610  * Originally Released Under LGPL - original licence link has changed is not relivant.
33611  *
33612  * Fork - LGPL
33613  * <script type="text/javascript">
33614  */
33615 /**
33616  * @class Roo.tree.TreeEditor
33617  * @extends Roo.Editor
33618  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
33619  * as the editor field.
33620  * @constructor
33621  * @param {Object} config (used to be the tree panel.)
33622  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
33623  * 
33624  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
33625  * @cfg {Roo.form.TextField|Object} field The field configuration
33626  *
33627  * 
33628  */
33629 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
33630     var tree = config;
33631     var field;
33632     if (oldconfig) { // old style..
33633         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
33634     } else {
33635         // new style..
33636         tree = config.tree;
33637         config.field = config.field  || {};
33638         config.field.xtype = 'TextField';
33639         field = Roo.factory(config.field, Roo.form);
33640     }
33641     config = config || {};
33642     
33643     
33644     this.addEvents({
33645         /**
33646          * @event beforenodeedit
33647          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
33648          * false from the handler of this event.
33649          * @param {Editor} this
33650          * @param {Roo.tree.Node} node 
33651          */
33652         "beforenodeedit" : true
33653     });
33654     
33655     //Roo.log(config);
33656     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
33657
33658     this.tree = tree;
33659
33660     tree.on('beforeclick', this.beforeNodeClick, this);
33661     tree.getTreeEl().on('mousedown', this.hide, this);
33662     this.on('complete', this.updateNode, this);
33663     this.on('beforestartedit', this.fitToTree, this);
33664     this.on('startedit', this.bindScroll, this, {delay:10});
33665     this.on('specialkey', this.onSpecialKey, this);
33666 };
33667
33668 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
33669     /**
33670      * @cfg {String} alignment
33671      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
33672      */
33673     alignment: "l-l",
33674     // inherit
33675     autoSize: false,
33676     /**
33677      * @cfg {Boolean} hideEl
33678      * True to hide the bound element while the editor is displayed (defaults to false)
33679      */
33680     hideEl : false,
33681     /**
33682      * @cfg {String} cls
33683      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
33684      */
33685     cls: "x-small-editor x-tree-editor",
33686     /**
33687      * @cfg {Boolean} shim
33688      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
33689      */
33690     shim:false,
33691     // inherit
33692     shadow:"frame",
33693     /**
33694      * @cfg {Number} maxWidth
33695      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
33696      * the containing tree element's size, it will be automatically limited for you to the container width, taking
33697      * scroll and client offsets into account prior to each edit.
33698      */
33699     maxWidth: 250,
33700
33701     editDelay : 350,
33702
33703     // private
33704     fitToTree : function(ed, el){
33705         var td = this.tree.getTreeEl().dom, nd = el.dom;
33706         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
33707             td.scrollLeft = nd.offsetLeft;
33708         }
33709         var w = Math.min(
33710                 this.maxWidth,
33711                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
33712         this.setSize(w, '');
33713         
33714         return this.fireEvent('beforenodeedit', this, this.editNode);
33715         
33716     },
33717
33718     // private
33719     triggerEdit : function(node){
33720         this.completeEdit();
33721         this.editNode = node;
33722         this.startEdit(node.ui.textNode, node.text);
33723     },
33724
33725     // private
33726     bindScroll : function(){
33727         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
33728     },
33729
33730     // private
33731     beforeNodeClick : function(node, e){
33732         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
33733         this.lastClick = new Date();
33734         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
33735             e.stopEvent();
33736             this.triggerEdit(node);
33737             return false;
33738         }
33739         return true;
33740     },
33741
33742     // private
33743     updateNode : function(ed, value){
33744         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
33745         this.editNode.setText(value);
33746     },
33747
33748     // private
33749     onHide : function(){
33750         Roo.tree.TreeEditor.superclass.onHide.call(this);
33751         if(this.editNode){
33752             this.editNode.ui.focus();
33753         }
33754     },
33755
33756     // private
33757     onSpecialKey : function(field, e){
33758         var k = e.getKey();
33759         if(k == e.ESC){
33760             e.stopEvent();
33761             this.cancelEdit();
33762         }else if(k == e.ENTER && !e.hasModifier()){
33763             e.stopEvent();
33764             this.completeEdit();
33765         }
33766     }
33767 });//<Script type="text/javascript">
33768 /*
33769  * Based on:
33770  * Ext JS Library 1.1.1
33771  * Copyright(c) 2006-2007, Ext JS, LLC.
33772  *
33773  * Originally Released Under LGPL - original licence link has changed is not relivant.
33774  *
33775  * Fork - LGPL
33776  * <script type="text/javascript">
33777  */
33778  
33779 /**
33780  * Not documented??? - probably should be...
33781  */
33782
33783 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
33784     //focus: Roo.emptyFn, // prevent odd scrolling behavior
33785     
33786     renderElements : function(n, a, targetNode, bulkRender){
33787         //consel.log("renderElements?");
33788         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
33789
33790         var t = n.getOwnerTree();
33791         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
33792         
33793         var cols = t.columns;
33794         var bw = t.borderWidth;
33795         var c = cols[0];
33796         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
33797          var cb = typeof a.checked == "boolean";
33798         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
33799         var colcls = 'x-t-' + tid + '-c0';
33800         var buf = [
33801             '<li class="x-tree-node">',
33802             
33803                 
33804                 '<div class="x-tree-node-el ', a.cls,'">',
33805                     // extran...
33806                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
33807                 
33808                 
33809                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
33810                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
33811                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
33812                            (a.icon ? ' x-tree-node-inline-icon' : ''),
33813                            (a.iconCls ? ' '+a.iconCls : ''),
33814                            '" unselectable="on" />',
33815                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
33816                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
33817                              
33818                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
33819                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
33820                             '<span unselectable="on" qtip="' + tx + '">',
33821                              tx,
33822                              '</span></a>' ,
33823                     '</div>',
33824                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
33825                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
33826                  ];
33827         for(var i = 1, len = cols.length; i < len; i++){
33828             c = cols[i];
33829             colcls = 'x-t-' + tid + '-c' +i;
33830             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
33831             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
33832                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
33833                       "</div>");
33834          }
33835          
33836          buf.push(
33837             '</a>',
33838             '<div class="x-clear"></div></div>',
33839             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
33840             "</li>");
33841         
33842         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
33843             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
33844                                 n.nextSibling.ui.getEl(), buf.join(""));
33845         }else{
33846             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
33847         }
33848         var el = this.wrap.firstChild;
33849         this.elRow = el;
33850         this.elNode = el.firstChild;
33851         this.ranchor = el.childNodes[1];
33852         this.ctNode = this.wrap.childNodes[1];
33853         var cs = el.firstChild.childNodes;
33854         this.indentNode = cs[0];
33855         this.ecNode = cs[1];
33856         this.iconNode = cs[2];
33857         var index = 3;
33858         if(cb){
33859             this.checkbox = cs[3];
33860             index++;
33861         }
33862         this.anchor = cs[index];
33863         
33864         this.textNode = cs[index].firstChild;
33865         
33866         //el.on("click", this.onClick, this);
33867         //el.on("dblclick", this.onDblClick, this);
33868         
33869         
33870        // console.log(this);
33871     },
33872     initEvents : function(){
33873         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
33874         
33875             
33876         var a = this.ranchor;
33877
33878         var el = Roo.get(a);
33879
33880         if(Roo.isOpera){ // opera render bug ignores the CSS
33881             el.setStyle("text-decoration", "none");
33882         }
33883
33884         el.on("click", this.onClick, this);
33885         el.on("dblclick", this.onDblClick, this);
33886         el.on("contextmenu", this.onContextMenu, this);
33887         
33888     },
33889     
33890     /*onSelectedChange : function(state){
33891         if(state){
33892             this.focus();
33893             this.addClass("x-tree-selected");
33894         }else{
33895             //this.blur();
33896             this.removeClass("x-tree-selected");
33897         }
33898     },*/
33899     addClass : function(cls){
33900         if(this.elRow){
33901             Roo.fly(this.elRow).addClass(cls);
33902         }
33903         
33904     },
33905     
33906     
33907     removeClass : function(cls){
33908         if(this.elRow){
33909             Roo.fly(this.elRow).removeClass(cls);
33910         }
33911     }
33912
33913     
33914     
33915 });//<Script type="text/javascript">
33916
33917 /*
33918  * Based on:
33919  * Ext JS Library 1.1.1
33920  * Copyright(c) 2006-2007, Ext JS, LLC.
33921  *
33922  * Originally Released Under LGPL - original licence link has changed is not relivant.
33923  *
33924  * Fork - LGPL
33925  * <script type="text/javascript">
33926  */
33927  
33928
33929 /**
33930  * @class Roo.tree.ColumnTree
33931  * @extends Roo.data.TreePanel
33932  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
33933  * @cfg {int} borderWidth  compined right/left border allowance
33934  * @constructor
33935  * @param {String/HTMLElement/Element} el The container element
33936  * @param {Object} config
33937  */
33938 Roo.tree.ColumnTree =  function(el, config)
33939 {
33940    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
33941    this.addEvents({
33942         /**
33943         * @event resize
33944         * Fire this event on a container when it resizes
33945         * @param {int} w Width
33946         * @param {int} h Height
33947         */
33948        "resize" : true
33949     });
33950     this.on('resize', this.onResize, this);
33951 };
33952
33953 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
33954     //lines:false,
33955     
33956     
33957     borderWidth: Roo.isBorderBox ? 0 : 2, 
33958     headEls : false,
33959     
33960     render : function(){
33961         // add the header.....
33962        
33963         Roo.tree.ColumnTree.superclass.render.apply(this);
33964         
33965         this.el.addClass('x-column-tree');
33966         
33967         this.headers = this.el.createChild(
33968             {cls:'x-tree-headers'},this.innerCt.dom);
33969    
33970         var cols = this.columns, c;
33971         var totalWidth = 0;
33972         this.headEls = [];
33973         var  len = cols.length;
33974         for(var i = 0; i < len; i++){
33975              c = cols[i];
33976              totalWidth += c.width;
33977             this.headEls.push(this.headers.createChild({
33978                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
33979                  cn: {
33980                      cls:'x-tree-hd-text',
33981                      html: c.header
33982                  },
33983                  style:'width:'+(c.width-this.borderWidth)+'px;'
33984              }));
33985         }
33986         this.headers.createChild({cls:'x-clear'});
33987         // prevent floats from wrapping when clipped
33988         this.headers.setWidth(totalWidth);
33989         //this.innerCt.setWidth(totalWidth);
33990         this.innerCt.setStyle({ overflow: 'auto' });
33991         this.onResize(this.width, this.height);
33992              
33993         
33994     },
33995     onResize : function(w,h)
33996     {
33997         this.height = h;
33998         this.width = w;
33999         // resize cols..
34000         this.innerCt.setWidth(this.width);
34001         this.innerCt.setHeight(this.height-20);
34002         
34003         // headers...
34004         var cols = this.columns, c;
34005         var totalWidth = 0;
34006         var expEl = false;
34007         var len = cols.length;
34008         for(var i = 0; i < len; i++){
34009             c = cols[i];
34010             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
34011                 // it's the expander..
34012                 expEl  = this.headEls[i];
34013                 continue;
34014             }
34015             totalWidth += c.width;
34016             
34017         }
34018         if (expEl) {
34019             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
34020         }
34021         this.headers.setWidth(w-20);
34022
34023         
34024         
34025         
34026     }
34027 });
34028 /*
34029  * Based on:
34030  * Ext JS Library 1.1.1
34031  * Copyright(c) 2006-2007, Ext JS, LLC.
34032  *
34033  * Originally Released Under LGPL - original licence link has changed is not relivant.
34034  *
34035  * Fork - LGPL
34036  * <script type="text/javascript">
34037  */
34038  
34039 /**
34040  * @class Roo.menu.Menu
34041  * @extends Roo.util.Observable
34042  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
34043  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
34044  * @constructor
34045  * Creates a new Menu
34046  * @param {Object} config Configuration options
34047  */
34048 Roo.menu.Menu = function(config){
34049     Roo.apply(this, config);
34050     this.id = this.id || Roo.id();
34051     this.addEvents({
34052         /**
34053          * @event beforeshow
34054          * Fires before this menu is displayed
34055          * @param {Roo.menu.Menu} this
34056          */
34057         beforeshow : true,
34058         /**
34059          * @event beforehide
34060          * Fires before this menu is hidden
34061          * @param {Roo.menu.Menu} this
34062          */
34063         beforehide : true,
34064         /**
34065          * @event show
34066          * Fires after this menu is displayed
34067          * @param {Roo.menu.Menu} this
34068          */
34069         show : true,
34070         /**
34071          * @event hide
34072          * Fires after this menu is hidden
34073          * @param {Roo.menu.Menu} this
34074          */
34075         hide : true,
34076         /**
34077          * @event click
34078          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
34079          * @param {Roo.menu.Menu} this
34080          * @param {Roo.menu.Item} menuItem The menu item that was clicked
34081          * @param {Roo.EventObject} e
34082          */
34083         click : true,
34084         /**
34085          * @event mouseover
34086          * Fires when the mouse is hovering over this menu
34087          * @param {Roo.menu.Menu} this
34088          * @param {Roo.EventObject} e
34089          * @param {Roo.menu.Item} menuItem The menu item that was clicked
34090          */
34091         mouseover : true,
34092         /**
34093          * @event mouseout
34094          * Fires when the mouse exits this menu
34095          * @param {Roo.menu.Menu} this
34096          * @param {Roo.EventObject} e
34097          * @param {Roo.menu.Item} menuItem The menu item that was clicked
34098          */
34099         mouseout : true,
34100         /**
34101          * @event itemclick
34102          * Fires when a menu item contained in this menu is clicked
34103          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
34104          * @param {Roo.EventObject} e
34105          */
34106         itemclick: true
34107     });
34108     if (this.registerMenu) {
34109         Roo.menu.MenuMgr.register(this);
34110     }
34111     
34112     var mis = this.items;
34113     this.items = new Roo.util.MixedCollection();
34114     if(mis){
34115         this.add.apply(this, mis);
34116     }
34117 };
34118
34119 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
34120     /**
34121      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
34122      */
34123     minWidth : 120,
34124     /**
34125      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
34126      * for bottom-right shadow (defaults to "sides")
34127      */
34128     shadow : "sides",
34129     /**
34130      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
34131      * this menu (defaults to "tl-tr?")
34132      */
34133     subMenuAlign : "tl-tr?",
34134     /**
34135      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
34136      * relative to its element of origin (defaults to "tl-bl?")
34137      */
34138     defaultAlign : "tl-bl?",
34139     /**
34140      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
34141      */
34142     allowOtherMenus : false,
34143     /**
34144      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
34145      */
34146     registerMenu : true,
34147
34148     hidden:true,
34149
34150     // private
34151     render : function(){
34152         if(this.el){
34153             return;
34154         }
34155         var el = this.el = new Roo.Layer({
34156             cls: "x-menu",
34157             shadow:this.shadow,
34158             constrain: false,
34159             parentEl: this.parentEl || document.body,
34160             zindex:15000
34161         });
34162
34163         this.keyNav = new Roo.menu.MenuNav(this);
34164
34165         if(this.plain){
34166             el.addClass("x-menu-plain");
34167         }
34168         if(this.cls){
34169             el.addClass(this.cls);
34170         }
34171         // generic focus element
34172         this.focusEl = el.createChild({
34173             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
34174         });
34175         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
34176         ul.on("click", this.onClick, this);
34177         ul.on("mouseover", this.onMouseOver, this);
34178         ul.on("mouseout", this.onMouseOut, this);
34179         this.items.each(function(item){
34180             var li = document.createElement("li");
34181             li.className = "x-menu-list-item";
34182             ul.dom.appendChild(li);
34183             item.render(li, this);
34184         }, this);
34185         this.ul = ul;
34186         this.autoWidth();
34187     },
34188
34189     // private
34190     autoWidth : function(){
34191         var el = this.el, ul = this.ul;
34192         if(!el){
34193             return;
34194         }
34195         var w = this.width;
34196         if(w){
34197             el.setWidth(w);
34198         }else if(Roo.isIE){
34199             el.setWidth(this.minWidth);
34200             var t = el.dom.offsetWidth; // force recalc
34201             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
34202         }
34203     },
34204
34205     // private
34206     delayAutoWidth : function(){
34207         if(this.rendered){
34208             if(!this.awTask){
34209                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
34210             }
34211             this.awTask.delay(20);
34212         }
34213     },
34214
34215     // private
34216     findTargetItem : function(e){
34217         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
34218         if(t && t.menuItemId){
34219             return this.items.get(t.menuItemId);
34220         }
34221     },
34222
34223     // private
34224     onClick : function(e){
34225         var t;
34226         if(t = this.findTargetItem(e)){
34227             t.onClick(e);
34228             this.fireEvent("click", this, t, e);
34229         }
34230     },
34231
34232     // private
34233     setActiveItem : function(item, autoExpand){
34234         if(item != this.activeItem){
34235             if(this.activeItem){
34236                 this.activeItem.deactivate();
34237             }
34238             this.activeItem = item;
34239             item.activate(autoExpand);
34240         }else if(autoExpand){
34241             item.expandMenu();
34242         }
34243     },
34244
34245     // private
34246     tryActivate : function(start, step){
34247         var items = this.items;
34248         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
34249             var item = items.get(i);
34250             if(!item.disabled && item.canActivate){
34251                 this.setActiveItem(item, false);
34252                 return item;
34253             }
34254         }
34255         return false;
34256     },
34257
34258     // private
34259     onMouseOver : function(e){
34260         var t;
34261         if(t = this.findTargetItem(e)){
34262             if(t.canActivate && !t.disabled){
34263                 this.setActiveItem(t, true);
34264             }
34265         }
34266         this.fireEvent("mouseover", this, e, t);
34267     },
34268
34269     // private
34270     onMouseOut : function(e){
34271         var t;
34272         if(t = this.findTargetItem(e)){
34273             if(t == this.activeItem && t.shouldDeactivate(e)){
34274                 this.activeItem.deactivate();
34275                 delete this.activeItem;
34276             }
34277         }
34278         this.fireEvent("mouseout", this, e, t);
34279     },
34280
34281     /**
34282      * Read-only.  Returns true if the menu is currently displayed, else false.
34283      * @type Boolean
34284      */
34285     isVisible : function(){
34286         return this.el && !this.hidden;
34287     },
34288
34289     /**
34290      * Displays this menu relative to another element
34291      * @param {String/HTMLElement/Roo.Element} element The element to align to
34292      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
34293      * the element (defaults to this.defaultAlign)
34294      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
34295      */
34296     show : function(el, pos, parentMenu){
34297         this.parentMenu = parentMenu;
34298         if(!this.el){
34299             this.render();
34300         }
34301         this.fireEvent("beforeshow", this);
34302         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
34303     },
34304
34305     /**
34306      * Displays this menu at a specific xy position
34307      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
34308      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
34309      */
34310     showAt : function(xy, parentMenu, /* private: */_e){
34311         this.parentMenu = parentMenu;
34312         if(!this.el){
34313             this.render();
34314         }
34315         if(_e !== false){
34316             this.fireEvent("beforeshow", this);
34317             xy = this.el.adjustForConstraints(xy);
34318         }
34319         this.el.setXY(xy);
34320         this.el.show();
34321         this.hidden = false;
34322         this.focus();
34323         this.fireEvent("show", this);
34324     },
34325
34326     focus : function(){
34327         if(!this.hidden){
34328             this.doFocus.defer(50, this);
34329         }
34330     },
34331
34332     doFocus : function(){
34333         if(!this.hidden){
34334             this.focusEl.focus();
34335         }
34336     },
34337
34338     /**
34339      * Hides this menu and optionally all parent menus
34340      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
34341      */
34342     hide : function(deep){
34343         if(this.el && this.isVisible()){
34344             this.fireEvent("beforehide", this);
34345             if(this.activeItem){
34346                 this.activeItem.deactivate();
34347                 this.activeItem = null;
34348             }
34349             this.el.hide();
34350             this.hidden = true;
34351             this.fireEvent("hide", this);
34352         }
34353         if(deep === true && this.parentMenu){
34354             this.parentMenu.hide(true);
34355         }
34356     },
34357
34358     /**
34359      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
34360      * Any of the following are valid:
34361      * <ul>
34362      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
34363      * <li>An HTMLElement object which will be converted to a menu item</li>
34364      * <li>A menu item config object that will be created as a new menu item</li>
34365      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
34366      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
34367      * </ul>
34368      * Usage:
34369      * <pre><code>
34370 // Create the menu
34371 var menu = new Roo.menu.Menu();
34372
34373 // Create a menu item to add by reference
34374 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
34375
34376 // Add a bunch of items at once using different methods.
34377 // Only the last item added will be returned.
34378 var item = menu.add(
34379     menuItem,                // add existing item by ref
34380     'Dynamic Item',          // new TextItem
34381     '-',                     // new separator
34382     { text: 'Config Item' }  // new item by config
34383 );
34384 </code></pre>
34385      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
34386      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
34387      */
34388     add : function(){
34389         var a = arguments, l = a.length, item;
34390         for(var i = 0; i < l; i++){
34391             var el = a[i];
34392             if ((typeof(el) == "object") && el.xtype && el.xns) {
34393                 el = Roo.factory(el, Roo.menu);
34394             }
34395             
34396             if(el.render){ // some kind of Item
34397                 item = this.addItem(el);
34398             }else if(typeof el == "string"){ // string
34399                 if(el == "separator" || el == "-"){
34400                     item = this.addSeparator();
34401                 }else{
34402                     item = this.addText(el);
34403                 }
34404             }else if(el.tagName || el.el){ // element
34405                 item = this.addElement(el);
34406             }else if(typeof el == "object"){ // must be menu item config?
34407                 item = this.addMenuItem(el);
34408             }
34409         }
34410         return item;
34411     },
34412
34413     /**
34414      * Returns this menu's underlying {@link Roo.Element} object
34415      * @return {Roo.Element} The element
34416      */
34417     getEl : function(){
34418         if(!this.el){
34419             this.render();
34420         }
34421         return this.el;
34422     },
34423
34424     /**
34425      * Adds a separator bar to the menu
34426      * @return {Roo.menu.Item} The menu item that was added
34427      */
34428     addSeparator : function(){
34429         return this.addItem(new Roo.menu.Separator());
34430     },
34431
34432     /**
34433      * Adds an {@link Roo.Element} object to the menu
34434      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
34435      * @return {Roo.menu.Item} The menu item that was added
34436      */
34437     addElement : function(el){
34438         return this.addItem(new Roo.menu.BaseItem(el));
34439     },
34440
34441     /**
34442      * Adds an existing object based on {@link Roo.menu.Item} to the menu
34443      * @param {Roo.menu.Item} item The menu item to add
34444      * @return {Roo.menu.Item} The menu item that was added
34445      */
34446     addItem : function(item){
34447         this.items.add(item);
34448         if(this.ul){
34449             var li = document.createElement("li");
34450             li.className = "x-menu-list-item";
34451             this.ul.dom.appendChild(li);
34452             item.render(li, this);
34453             this.delayAutoWidth();
34454         }
34455         return item;
34456     },
34457
34458     /**
34459      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
34460      * @param {Object} config A MenuItem config object
34461      * @return {Roo.menu.Item} The menu item that was added
34462      */
34463     addMenuItem : function(config){
34464         if(!(config instanceof Roo.menu.Item)){
34465             if(typeof config.checked == "boolean"){ // must be check menu item config?
34466                 config = new Roo.menu.CheckItem(config);
34467             }else{
34468                 config = new Roo.menu.Item(config);
34469             }
34470         }
34471         return this.addItem(config);
34472     },
34473
34474     /**
34475      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
34476      * @param {String} text The text to display in the menu item
34477      * @return {Roo.menu.Item} The menu item that was added
34478      */
34479     addText : function(text){
34480         return this.addItem(new Roo.menu.TextItem({ text : text }));
34481     },
34482
34483     /**
34484      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
34485      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
34486      * @param {Roo.menu.Item} item The menu item to add
34487      * @return {Roo.menu.Item} The menu item that was added
34488      */
34489     insert : function(index, item){
34490         this.items.insert(index, item);
34491         if(this.ul){
34492             var li = document.createElement("li");
34493             li.className = "x-menu-list-item";
34494             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
34495             item.render(li, this);
34496             this.delayAutoWidth();
34497         }
34498         return item;
34499     },
34500
34501     /**
34502      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
34503      * @param {Roo.menu.Item} item The menu item to remove
34504      */
34505     remove : function(item){
34506         this.items.removeKey(item.id);
34507         item.destroy();
34508     },
34509
34510     /**
34511      * Removes and destroys all items in the menu
34512      */
34513     removeAll : function(){
34514         var f;
34515         while(f = this.items.first()){
34516             this.remove(f);
34517         }
34518     }
34519 });
34520
34521 // MenuNav is a private utility class used internally by the Menu
34522 Roo.menu.MenuNav = function(menu){
34523     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
34524     this.scope = this.menu = menu;
34525 };
34526
34527 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
34528     doRelay : function(e, h){
34529         var k = e.getKey();
34530         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
34531             this.menu.tryActivate(0, 1);
34532             return false;
34533         }
34534         return h.call(this.scope || this, e, this.menu);
34535     },
34536
34537     up : function(e, m){
34538         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
34539             m.tryActivate(m.items.length-1, -1);
34540         }
34541     },
34542
34543     down : function(e, m){
34544         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
34545             m.tryActivate(0, 1);
34546         }
34547     },
34548
34549     right : function(e, m){
34550         if(m.activeItem){
34551             m.activeItem.expandMenu(true);
34552         }
34553     },
34554
34555     left : function(e, m){
34556         m.hide();
34557         if(m.parentMenu && m.parentMenu.activeItem){
34558             m.parentMenu.activeItem.activate();
34559         }
34560     },
34561
34562     enter : function(e, m){
34563         if(m.activeItem){
34564             e.stopPropagation();
34565             m.activeItem.onClick(e);
34566             m.fireEvent("click", this, m.activeItem);
34567             return true;
34568         }
34569     }
34570 });/*
34571  * Based on:
34572  * Ext JS Library 1.1.1
34573  * Copyright(c) 2006-2007, Ext JS, LLC.
34574  *
34575  * Originally Released Under LGPL - original licence link has changed is not relivant.
34576  *
34577  * Fork - LGPL
34578  * <script type="text/javascript">
34579  */
34580  
34581 /**
34582  * @class Roo.menu.MenuMgr
34583  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
34584  * @singleton
34585  */
34586 Roo.menu.MenuMgr = function(){
34587    var menus, active, groups = {}, attached = false, lastShow = new Date();
34588
34589    // private - called when first menu is created
34590    function init(){
34591        menus = {};
34592        active = new Roo.util.MixedCollection();
34593        Roo.get(document).addKeyListener(27, function(){
34594            if(active.length > 0){
34595                hideAll();
34596            }
34597        });
34598    }
34599
34600    // private
34601    function hideAll(){
34602        if(active && active.length > 0){
34603            var c = active.clone();
34604            c.each(function(m){
34605                m.hide();
34606            });
34607        }
34608    }
34609
34610    // private
34611    function onHide(m){
34612        active.remove(m);
34613        if(active.length < 1){
34614            Roo.get(document).un("mousedown", onMouseDown);
34615            attached = false;
34616        }
34617    }
34618
34619    // private
34620    function onShow(m){
34621        var last = active.last();
34622        lastShow = new Date();
34623        active.add(m);
34624        if(!attached){
34625            Roo.get(document).on("mousedown", onMouseDown);
34626            attached = true;
34627        }
34628        if(m.parentMenu){
34629           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
34630           m.parentMenu.activeChild = m;
34631        }else if(last && last.isVisible()){
34632           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
34633        }
34634    }
34635
34636    // private
34637    function onBeforeHide(m){
34638        if(m.activeChild){
34639            m.activeChild.hide();
34640        }
34641        if(m.autoHideTimer){
34642            clearTimeout(m.autoHideTimer);
34643            delete m.autoHideTimer;
34644        }
34645    }
34646
34647    // private
34648    function onBeforeShow(m){
34649        var pm = m.parentMenu;
34650        if(!pm && !m.allowOtherMenus){
34651            hideAll();
34652        }else if(pm && pm.activeChild && active != m){
34653            pm.activeChild.hide();
34654        }
34655    }
34656
34657    // private
34658    function onMouseDown(e){
34659        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
34660            hideAll();
34661        }
34662    }
34663
34664    // private
34665    function onBeforeCheck(mi, state){
34666        if(state){
34667            var g = groups[mi.group];
34668            for(var i = 0, l = g.length; i < l; i++){
34669                if(g[i] != mi){
34670                    g[i].setChecked(false);
34671                }
34672            }
34673        }
34674    }
34675
34676    return {
34677
34678        /**
34679         * Hides all menus that are currently visible
34680         */
34681        hideAll : function(){
34682             hideAll();  
34683        },
34684
34685        // private
34686        register : function(menu){
34687            if(!menus){
34688                init();
34689            }
34690            menus[menu.id] = menu;
34691            menu.on("beforehide", onBeforeHide);
34692            menu.on("hide", onHide);
34693            menu.on("beforeshow", onBeforeShow);
34694            menu.on("show", onShow);
34695            var g = menu.group;
34696            if(g && menu.events["checkchange"]){
34697                if(!groups[g]){
34698                    groups[g] = [];
34699                }
34700                groups[g].push(menu);
34701                menu.on("checkchange", onCheck);
34702            }
34703        },
34704
34705         /**
34706          * Returns a {@link Roo.menu.Menu} object
34707          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
34708          * be used to generate and return a new Menu instance.
34709          */
34710        get : function(menu){
34711            if(typeof menu == "string"){ // menu id
34712                return menus[menu];
34713            }else if(menu.events){  // menu instance
34714                return menu;
34715            }else if(typeof menu.length == 'number'){ // array of menu items?
34716                return new Roo.menu.Menu({items:menu});
34717            }else{ // otherwise, must be a config
34718                return new Roo.menu.Menu(menu);
34719            }
34720        },
34721
34722        // private
34723        unregister : function(menu){
34724            delete menus[menu.id];
34725            menu.un("beforehide", onBeforeHide);
34726            menu.un("hide", onHide);
34727            menu.un("beforeshow", onBeforeShow);
34728            menu.un("show", onShow);
34729            var g = menu.group;
34730            if(g && menu.events["checkchange"]){
34731                groups[g].remove(menu);
34732                menu.un("checkchange", onCheck);
34733            }
34734        },
34735
34736        // private
34737        registerCheckable : function(menuItem){
34738            var g = menuItem.group;
34739            if(g){
34740                if(!groups[g]){
34741                    groups[g] = [];
34742                }
34743                groups[g].push(menuItem);
34744                menuItem.on("beforecheckchange", onBeforeCheck);
34745            }
34746        },
34747
34748        // private
34749        unregisterCheckable : function(menuItem){
34750            var g = menuItem.group;
34751            if(g){
34752                groups[g].remove(menuItem);
34753                menuItem.un("beforecheckchange", onBeforeCheck);
34754            }
34755        }
34756    };
34757 }();/*
34758  * Based on:
34759  * Ext JS Library 1.1.1
34760  * Copyright(c) 2006-2007, Ext JS, LLC.
34761  *
34762  * Originally Released Under LGPL - original licence link has changed is not relivant.
34763  *
34764  * Fork - LGPL
34765  * <script type="text/javascript">
34766  */
34767  
34768
34769 /**
34770  * @class Roo.menu.BaseItem
34771  * @extends Roo.Component
34772  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
34773  * management and base configuration options shared by all menu components.
34774  * @constructor
34775  * Creates a new BaseItem
34776  * @param {Object} config Configuration options
34777  */
34778 Roo.menu.BaseItem = function(config){
34779     Roo.menu.BaseItem.superclass.constructor.call(this, config);
34780
34781     this.addEvents({
34782         /**
34783          * @event click
34784          * Fires when this item is clicked
34785          * @param {Roo.menu.BaseItem} this
34786          * @param {Roo.EventObject} e
34787          */
34788         click: true,
34789         /**
34790          * @event activate
34791          * Fires when this item is activated
34792          * @param {Roo.menu.BaseItem} this
34793          */
34794         activate : true,
34795         /**
34796          * @event deactivate
34797          * Fires when this item is deactivated
34798          * @param {Roo.menu.BaseItem} this
34799          */
34800         deactivate : true
34801     });
34802
34803     if(this.handler){
34804         this.on("click", this.handler, this.scope, true);
34805     }
34806 };
34807
34808 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
34809     /**
34810      * @cfg {Function} handler
34811      * A function that will handle the click event of this menu item (defaults to undefined)
34812      */
34813     /**
34814      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
34815      */
34816     canActivate : false,
34817     /**
34818      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
34819      */
34820     activeClass : "x-menu-item-active",
34821     /**
34822      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
34823      */
34824     hideOnClick : true,
34825     /**
34826      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
34827      */
34828     hideDelay : 100,
34829
34830     // private
34831     ctype: "Roo.menu.BaseItem",
34832
34833     // private
34834     actionMode : "container",
34835
34836     // private
34837     render : function(container, parentMenu){
34838         this.parentMenu = parentMenu;
34839         Roo.menu.BaseItem.superclass.render.call(this, container);
34840         this.container.menuItemId = this.id;
34841     },
34842
34843     // private
34844     onRender : function(container, position){
34845         this.el = Roo.get(this.el);
34846         container.dom.appendChild(this.el.dom);
34847     },
34848
34849     // private
34850     onClick : function(e){
34851         if(!this.disabled && this.fireEvent("click", this, e) !== false
34852                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
34853             this.handleClick(e);
34854         }else{
34855             e.stopEvent();
34856         }
34857     },
34858
34859     // private
34860     activate : function(){
34861         if(this.disabled){
34862             return false;
34863         }
34864         var li = this.container;
34865         li.addClass(this.activeClass);
34866         this.region = li.getRegion().adjust(2, 2, -2, -2);
34867         this.fireEvent("activate", this);
34868         return true;
34869     },
34870
34871     // private
34872     deactivate : function(){
34873         this.container.removeClass(this.activeClass);
34874         this.fireEvent("deactivate", this);
34875     },
34876
34877     // private
34878     shouldDeactivate : function(e){
34879         return !this.region || !this.region.contains(e.getPoint());
34880     },
34881
34882     // private
34883     handleClick : function(e){
34884         if(this.hideOnClick){
34885             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
34886         }
34887     },
34888
34889     // private
34890     expandMenu : function(autoActivate){
34891         // do nothing
34892     },
34893
34894     // private
34895     hideMenu : function(){
34896         // do nothing
34897     }
34898 });/*
34899  * Based on:
34900  * Ext JS Library 1.1.1
34901  * Copyright(c) 2006-2007, Ext JS, LLC.
34902  *
34903  * Originally Released Under LGPL - original licence link has changed is not relivant.
34904  *
34905  * Fork - LGPL
34906  * <script type="text/javascript">
34907  */
34908  
34909 /**
34910  * @class Roo.menu.Adapter
34911  * @extends Roo.menu.BaseItem
34912  * 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.
34913  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
34914  * @constructor
34915  * Creates a new Adapter
34916  * @param {Object} config Configuration options
34917  */
34918 Roo.menu.Adapter = function(component, config){
34919     Roo.menu.Adapter.superclass.constructor.call(this, config);
34920     this.component = component;
34921 };
34922 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
34923     // private
34924     canActivate : true,
34925
34926     // private
34927     onRender : function(container, position){
34928         this.component.render(container);
34929         this.el = this.component.getEl();
34930     },
34931
34932     // private
34933     activate : function(){
34934         if(this.disabled){
34935             return false;
34936         }
34937         this.component.focus();
34938         this.fireEvent("activate", this);
34939         return true;
34940     },
34941
34942     // private
34943     deactivate : function(){
34944         this.fireEvent("deactivate", this);
34945     },
34946
34947     // private
34948     disable : function(){
34949         this.component.disable();
34950         Roo.menu.Adapter.superclass.disable.call(this);
34951     },
34952
34953     // private
34954     enable : function(){
34955         this.component.enable();
34956         Roo.menu.Adapter.superclass.enable.call(this);
34957     }
34958 });/*
34959  * Based on:
34960  * Ext JS Library 1.1.1
34961  * Copyright(c) 2006-2007, Ext JS, LLC.
34962  *
34963  * Originally Released Under LGPL - original licence link has changed is not relivant.
34964  *
34965  * Fork - LGPL
34966  * <script type="text/javascript">
34967  */
34968
34969 /**
34970  * @class Roo.menu.TextItem
34971  * @extends Roo.menu.BaseItem
34972  * Adds a static text string to a menu, usually used as either a heading or group separator.
34973  * Note: old style constructor with text is still supported.
34974  * 
34975  * @constructor
34976  * Creates a new TextItem
34977  * @param {Object} cfg Configuration
34978  */
34979 Roo.menu.TextItem = function(cfg){
34980     if (typeof(cfg) == 'string') {
34981         this.text = cfg;
34982     } else {
34983         Roo.apply(this,cfg);
34984     }
34985     
34986     Roo.menu.TextItem.superclass.constructor.call(this);
34987 };
34988
34989 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
34990     /**
34991      * @cfg {Boolean} text Text to show on item.
34992      */
34993     text : '',
34994     
34995     /**
34996      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
34997      */
34998     hideOnClick : false,
34999     /**
35000      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
35001      */
35002     itemCls : "x-menu-text",
35003
35004     // private
35005     onRender : function(){
35006         var s = document.createElement("span");
35007         s.className = this.itemCls;
35008         s.innerHTML = this.text;
35009         this.el = s;
35010         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
35011     }
35012 });/*
35013  * Based on:
35014  * Ext JS Library 1.1.1
35015  * Copyright(c) 2006-2007, Ext JS, LLC.
35016  *
35017  * Originally Released Under LGPL - original licence link has changed is not relivant.
35018  *
35019  * Fork - LGPL
35020  * <script type="text/javascript">
35021  */
35022
35023 /**
35024  * @class Roo.menu.Separator
35025  * @extends Roo.menu.BaseItem
35026  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
35027  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
35028  * @constructor
35029  * @param {Object} config Configuration options
35030  */
35031 Roo.menu.Separator = function(config){
35032     Roo.menu.Separator.superclass.constructor.call(this, config);
35033 };
35034
35035 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
35036     /**
35037      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
35038      */
35039     itemCls : "x-menu-sep",
35040     /**
35041      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
35042      */
35043     hideOnClick : false,
35044
35045     // private
35046     onRender : function(li){
35047         var s = document.createElement("span");
35048         s.className = this.itemCls;
35049         s.innerHTML = "&#160;";
35050         this.el = s;
35051         li.addClass("x-menu-sep-li");
35052         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
35053     }
35054 });/*
35055  * Based on:
35056  * Ext JS Library 1.1.1
35057  * Copyright(c) 2006-2007, Ext JS, LLC.
35058  *
35059  * Originally Released Under LGPL - original licence link has changed is not relivant.
35060  *
35061  * Fork - LGPL
35062  * <script type="text/javascript">
35063  */
35064 /**
35065  * @class Roo.menu.Item
35066  * @extends Roo.menu.BaseItem
35067  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
35068  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
35069  * activation and click handling.
35070  * @constructor
35071  * Creates a new Item
35072  * @param {Object} config Configuration options
35073  */
35074 Roo.menu.Item = function(config){
35075     Roo.menu.Item.superclass.constructor.call(this, config);
35076     if(this.menu){
35077         this.menu = Roo.menu.MenuMgr.get(this.menu);
35078     }
35079 };
35080 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
35081     
35082     /**
35083      * @cfg {String} text
35084      * The text to show on the menu item.
35085      */
35086     text: '',
35087      /**
35088      * @cfg {String} HTML to render in menu
35089      * The text to show on the menu item (HTML version).
35090      */
35091     html: '',
35092     /**
35093      * @cfg {String} icon
35094      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
35095      */
35096     icon: undefined,
35097     /**
35098      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
35099      */
35100     itemCls : "x-menu-item",
35101     /**
35102      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
35103      */
35104     canActivate : true,
35105     /**
35106      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
35107      */
35108     showDelay: 200,
35109     // doc'd in BaseItem
35110     hideDelay: 200,
35111
35112     // private
35113     ctype: "Roo.menu.Item",
35114     
35115     // private
35116     onRender : function(container, position){
35117         var el = document.createElement("a");
35118         el.hideFocus = true;
35119         el.unselectable = "on";
35120         el.href = this.href || "#";
35121         if(this.hrefTarget){
35122             el.target = this.hrefTarget;
35123         }
35124         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
35125         
35126         var html = this.html.length ? this.html  : String.format('{0}',this.text);
35127         
35128         el.innerHTML = String.format(
35129                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
35130                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
35131         this.el = el;
35132         Roo.menu.Item.superclass.onRender.call(this, container, position);
35133     },
35134
35135     /**
35136      * Sets the text to display in this menu item
35137      * @param {String} text The text to display
35138      * @param {Boolean} isHTML true to indicate text is pure html.
35139      */
35140     setText : function(text, isHTML){
35141         if (isHTML) {
35142             this.html = text;
35143         } else {
35144             this.text = text;
35145             this.html = '';
35146         }
35147         if(this.rendered){
35148             var html = this.html.length ? this.html  : String.format('{0}',this.text);
35149      
35150             this.el.update(String.format(
35151                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
35152                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
35153             this.parentMenu.autoWidth();
35154         }
35155     },
35156
35157     // private
35158     handleClick : function(e){
35159         if(!this.href){ // if no link defined, stop the event automatically
35160             e.stopEvent();
35161         }
35162         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
35163     },
35164
35165     // private
35166     activate : function(autoExpand){
35167         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
35168             this.focus();
35169             if(autoExpand){
35170                 this.expandMenu();
35171             }
35172         }
35173         return true;
35174     },
35175
35176     // private
35177     shouldDeactivate : function(e){
35178         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
35179             if(this.menu && this.menu.isVisible()){
35180                 return !this.menu.getEl().getRegion().contains(e.getPoint());
35181             }
35182             return true;
35183         }
35184         return false;
35185     },
35186
35187     // private
35188     deactivate : function(){
35189         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
35190         this.hideMenu();
35191     },
35192
35193     // private
35194     expandMenu : function(autoActivate){
35195         if(!this.disabled && this.menu){
35196             clearTimeout(this.hideTimer);
35197             delete this.hideTimer;
35198             if(!this.menu.isVisible() && !this.showTimer){
35199                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
35200             }else if (this.menu.isVisible() && autoActivate){
35201                 this.menu.tryActivate(0, 1);
35202             }
35203         }
35204     },
35205
35206     // private
35207     deferExpand : function(autoActivate){
35208         delete this.showTimer;
35209         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
35210         if(autoActivate){
35211             this.menu.tryActivate(0, 1);
35212         }
35213     },
35214
35215     // private
35216     hideMenu : function(){
35217         clearTimeout(this.showTimer);
35218         delete this.showTimer;
35219         if(!this.hideTimer && this.menu && this.menu.isVisible()){
35220             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
35221         }
35222     },
35223
35224     // private
35225     deferHide : function(){
35226         delete this.hideTimer;
35227         this.menu.hide();
35228     }
35229 });/*
35230  * Based on:
35231  * Ext JS Library 1.1.1
35232  * Copyright(c) 2006-2007, Ext JS, LLC.
35233  *
35234  * Originally Released Under LGPL - original licence link has changed is not relivant.
35235  *
35236  * Fork - LGPL
35237  * <script type="text/javascript">
35238  */
35239  
35240 /**
35241  * @class Roo.menu.CheckItem
35242  * @extends Roo.menu.Item
35243  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
35244  * @constructor
35245  * Creates a new CheckItem
35246  * @param {Object} config Configuration options
35247  */
35248 Roo.menu.CheckItem = function(config){
35249     Roo.menu.CheckItem.superclass.constructor.call(this, config);
35250     this.addEvents({
35251         /**
35252          * @event beforecheckchange
35253          * Fires before the checked value is set, providing an opportunity to cancel if needed
35254          * @param {Roo.menu.CheckItem} this
35255          * @param {Boolean} checked The new checked value that will be set
35256          */
35257         "beforecheckchange" : true,
35258         /**
35259          * @event checkchange
35260          * Fires after the checked value has been set
35261          * @param {Roo.menu.CheckItem} this
35262          * @param {Boolean} checked The checked value that was set
35263          */
35264         "checkchange" : true
35265     });
35266     if(this.checkHandler){
35267         this.on('checkchange', this.checkHandler, this.scope);
35268     }
35269 };
35270 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
35271     /**
35272      * @cfg {String} group
35273      * All check items with the same group name will automatically be grouped into a single-select
35274      * radio button group (defaults to '')
35275      */
35276     /**
35277      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
35278      */
35279     itemCls : "x-menu-item x-menu-check-item",
35280     /**
35281      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
35282      */
35283     groupClass : "x-menu-group-item",
35284
35285     /**
35286      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
35287      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
35288      * initialized with checked = true will be rendered as checked.
35289      */
35290     checked: false,
35291
35292     // private
35293     ctype: "Roo.menu.CheckItem",
35294
35295     // private
35296     onRender : function(c){
35297         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
35298         if(this.group){
35299             this.el.addClass(this.groupClass);
35300         }
35301         Roo.menu.MenuMgr.registerCheckable(this);
35302         if(this.checked){
35303             this.checked = false;
35304             this.setChecked(true, true);
35305         }
35306     },
35307
35308     // private
35309     destroy : function(){
35310         if(this.rendered){
35311             Roo.menu.MenuMgr.unregisterCheckable(this);
35312         }
35313         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
35314     },
35315
35316     /**
35317      * Set the checked state of this item
35318      * @param {Boolean} checked The new checked value
35319      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
35320      */
35321     setChecked : function(state, suppressEvent){
35322         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
35323             if(this.container){
35324                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
35325             }
35326             this.checked = state;
35327             if(suppressEvent !== true){
35328                 this.fireEvent("checkchange", this, state);
35329             }
35330         }
35331     },
35332
35333     // private
35334     handleClick : function(e){
35335        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
35336            this.setChecked(!this.checked);
35337        }
35338        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
35339     }
35340 });/*
35341  * Based on:
35342  * Ext JS Library 1.1.1
35343  * Copyright(c) 2006-2007, Ext JS, LLC.
35344  *
35345  * Originally Released Under LGPL - original licence link has changed is not relivant.
35346  *
35347  * Fork - LGPL
35348  * <script type="text/javascript">
35349  */
35350  
35351 /**
35352  * @class Roo.menu.DateItem
35353  * @extends Roo.menu.Adapter
35354  * A menu item that wraps the {@link Roo.DatPicker} component.
35355  * @constructor
35356  * Creates a new DateItem
35357  * @param {Object} config Configuration options
35358  */
35359 Roo.menu.DateItem = function(config){
35360     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
35361     /** The Roo.DatePicker object @type Roo.DatePicker */
35362     this.picker = this.component;
35363     this.addEvents({select: true});
35364     
35365     this.picker.on("render", function(picker){
35366         picker.getEl().swallowEvent("click");
35367         picker.container.addClass("x-menu-date-item");
35368     });
35369
35370     this.picker.on("select", this.onSelect, this);
35371 };
35372
35373 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
35374     // private
35375     onSelect : function(picker, date){
35376         this.fireEvent("select", this, date, picker);
35377         Roo.menu.DateItem.superclass.handleClick.call(this);
35378     }
35379 });/*
35380  * Based on:
35381  * Ext JS Library 1.1.1
35382  * Copyright(c) 2006-2007, Ext JS, LLC.
35383  *
35384  * Originally Released Under LGPL - original licence link has changed is not relivant.
35385  *
35386  * Fork - LGPL
35387  * <script type="text/javascript">
35388  */
35389  
35390 /**
35391  * @class Roo.menu.ColorItem
35392  * @extends Roo.menu.Adapter
35393  * A menu item that wraps the {@link Roo.ColorPalette} component.
35394  * @constructor
35395  * Creates a new ColorItem
35396  * @param {Object} config Configuration options
35397  */
35398 Roo.menu.ColorItem = function(config){
35399     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
35400     /** The Roo.ColorPalette object @type Roo.ColorPalette */
35401     this.palette = this.component;
35402     this.relayEvents(this.palette, ["select"]);
35403     if(this.selectHandler){
35404         this.on('select', this.selectHandler, this.scope);
35405     }
35406 };
35407 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
35408  * Based on:
35409  * Ext JS Library 1.1.1
35410  * Copyright(c) 2006-2007, Ext JS, LLC.
35411  *
35412  * Originally Released Under LGPL - original licence link has changed is not relivant.
35413  *
35414  * Fork - LGPL
35415  * <script type="text/javascript">
35416  */
35417  
35418
35419 /**
35420  * @class Roo.menu.DateMenu
35421  * @extends Roo.menu.Menu
35422  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
35423  * @constructor
35424  * Creates a new DateMenu
35425  * @param {Object} config Configuration options
35426  */
35427 Roo.menu.DateMenu = function(config){
35428     Roo.menu.DateMenu.superclass.constructor.call(this, config);
35429     this.plain = true;
35430     var di = new Roo.menu.DateItem(config);
35431     this.add(di);
35432     /**
35433      * The {@link Roo.DatePicker} instance for this DateMenu
35434      * @type DatePicker
35435      */
35436     this.picker = di.picker;
35437     /**
35438      * @event select
35439      * @param {DatePicker} picker
35440      * @param {Date} date
35441      */
35442     this.relayEvents(di, ["select"]);
35443
35444     this.on('beforeshow', function(){
35445         if(this.picker){
35446             this.picker.hideMonthPicker(true);
35447         }
35448     }, this);
35449 };
35450 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
35451     cls:'x-date-menu'
35452 });/*
35453  * Based on:
35454  * Ext JS Library 1.1.1
35455  * Copyright(c) 2006-2007, Ext JS, LLC.
35456  *
35457  * Originally Released Under LGPL - original licence link has changed is not relivant.
35458  *
35459  * Fork - LGPL
35460  * <script type="text/javascript">
35461  */
35462  
35463
35464 /**
35465  * @class Roo.menu.ColorMenu
35466  * @extends Roo.menu.Menu
35467  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
35468  * @constructor
35469  * Creates a new ColorMenu
35470  * @param {Object} config Configuration options
35471  */
35472 Roo.menu.ColorMenu = function(config){
35473     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
35474     this.plain = true;
35475     var ci = new Roo.menu.ColorItem(config);
35476     this.add(ci);
35477     /**
35478      * The {@link Roo.ColorPalette} instance for this ColorMenu
35479      * @type ColorPalette
35480      */
35481     this.palette = ci.palette;
35482     /**
35483      * @event select
35484      * @param {ColorPalette} palette
35485      * @param {String} color
35486      */
35487     this.relayEvents(ci, ["select"]);
35488 };
35489 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
35490  * Based on:
35491  * Ext JS Library 1.1.1
35492  * Copyright(c) 2006-2007, Ext JS, LLC.
35493  *
35494  * Originally Released Under LGPL - original licence link has changed is not relivant.
35495  *
35496  * Fork - LGPL
35497  * <script type="text/javascript">
35498  */
35499  
35500 /**
35501  * @class Roo.form.Field
35502  * @extends Roo.BoxComponent
35503  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
35504  * @constructor
35505  * Creates a new Field
35506  * @param {Object} config Configuration options
35507  */
35508 Roo.form.Field = function(config){
35509     Roo.form.Field.superclass.constructor.call(this, config);
35510 };
35511
35512 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
35513     /**
35514      * @cfg {String} fieldLabel Label to use when rendering a form.
35515      */
35516        /**
35517      * @cfg {String} qtip Mouse over tip
35518      */
35519      
35520     /**
35521      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
35522      */
35523     invalidClass : "x-form-invalid",
35524     /**
35525      * @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")
35526      */
35527     invalidText : "The value in this field is invalid",
35528     /**
35529      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
35530      */
35531     focusClass : "x-form-focus",
35532     /**
35533      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
35534       automatic validation (defaults to "keyup").
35535      */
35536     validationEvent : "keyup",
35537     /**
35538      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
35539      */
35540     validateOnBlur : true,
35541     /**
35542      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
35543      */
35544     validationDelay : 250,
35545     /**
35546      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
35547      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
35548      */
35549     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
35550     /**
35551      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
35552      */
35553     fieldClass : "x-form-field",
35554     /**
35555      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
35556      *<pre>
35557 Value         Description
35558 -----------   ----------------------------------------------------------------------
35559 qtip          Display a quick tip when the user hovers over the field
35560 title         Display a default browser title attribute popup
35561 under         Add a block div beneath the field containing the error text
35562 side          Add an error icon to the right of the field with a popup on hover
35563 [element id]  Add the error text directly to the innerHTML of the specified element
35564 </pre>
35565      */
35566     msgTarget : 'qtip',
35567     /**
35568      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
35569      */
35570     msgFx : 'normal',
35571
35572     /**
35573      * @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.
35574      */
35575     readOnly : false,
35576
35577     /**
35578      * @cfg {Boolean} disabled True to disable the field (defaults to false).
35579      */
35580     disabled : false,
35581
35582     /**
35583      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
35584      */
35585     inputType : undefined,
35586     
35587     /**
35588      * @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).
35589          */
35590         tabIndex : undefined,
35591         
35592     // private
35593     isFormField : true,
35594
35595     // private
35596     hasFocus : false,
35597     /**
35598      * @property {Roo.Element} fieldEl
35599      * Element Containing the rendered Field (with label etc.)
35600      */
35601     /**
35602      * @cfg {Mixed} value A value to initialize this field with.
35603      */
35604     value : undefined,
35605
35606     /**
35607      * @cfg {String} name The field's HTML name attribute.
35608      */
35609     /**
35610      * @cfg {String} cls A CSS class to apply to the field's underlying element.
35611      */
35612
35613         // private ??
35614         initComponent : function(){
35615         Roo.form.Field.superclass.initComponent.call(this);
35616         this.addEvents({
35617             /**
35618              * @event focus
35619              * Fires when this field receives input focus.
35620              * @param {Roo.form.Field} this
35621              */
35622             focus : true,
35623             /**
35624              * @event blur
35625              * Fires when this field loses input focus.
35626              * @param {Roo.form.Field} this
35627              */
35628             blur : true,
35629             /**
35630              * @event specialkey
35631              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
35632              * {@link Roo.EventObject#getKey} to determine which key was pressed.
35633              * @param {Roo.form.Field} this
35634              * @param {Roo.EventObject} e The event object
35635              */
35636             specialkey : true,
35637             /**
35638              * @event change
35639              * Fires just before the field blurs if the field value has changed.
35640              * @param {Roo.form.Field} this
35641              * @param {Mixed} newValue The new value
35642              * @param {Mixed} oldValue The original value
35643              */
35644             change : true,
35645             /**
35646              * @event invalid
35647              * Fires after the field has been marked as invalid.
35648              * @param {Roo.form.Field} this
35649              * @param {String} msg The validation message
35650              */
35651             invalid : true,
35652             /**
35653              * @event valid
35654              * Fires after the field has been validated with no errors.
35655              * @param {Roo.form.Field} this
35656              */
35657             valid : true,
35658              /**
35659              * @event keyup
35660              * Fires after the key up
35661              * @param {Roo.form.Field} this
35662              * @param {Roo.EventObject}  e The event Object
35663              */
35664             keyup : true
35665         });
35666     },
35667
35668     /**
35669      * Returns the name attribute of the field if available
35670      * @return {String} name The field name
35671      */
35672     getName: function(){
35673          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
35674     },
35675
35676     // private
35677     onRender : function(ct, position){
35678         Roo.form.Field.superclass.onRender.call(this, ct, position);
35679         if(!this.el){
35680             var cfg = this.getAutoCreate();
35681             if(!cfg.name){
35682                 cfg.name = this.name || this.id;
35683             }
35684             if(this.inputType){
35685                 cfg.type = this.inputType;
35686             }
35687             this.el = ct.createChild(cfg, position);
35688         }
35689         var type = this.el.dom.type;
35690         if(type){
35691             if(type == 'password'){
35692                 type = 'text';
35693             }
35694             this.el.addClass('x-form-'+type);
35695         }
35696         if(this.readOnly){
35697             this.el.dom.readOnly = true;
35698         }
35699         if(this.tabIndex !== undefined){
35700             this.el.dom.setAttribute('tabIndex', this.tabIndex);
35701         }
35702
35703         this.el.addClass([this.fieldClass, this.cls]);
35704         this.initValue();
35705     },
35706
35707     /**
35708      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
35709      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
35710      * @return {Roo.form.Field} this
35711      */
35712     applyTo : function(target){
35713         this.allowDomMove = false;
35714         this.el = Roo.get(target);
35715         this.render(this.el.dom.parentNode);
35716         return this;
35717     },
35718
35719     // private
35720     initValue : function(){
35721         if(this.value !== undefined){
35722             this.setValue(this.value);
35723         }else if(this.el.dom.value.length > 0){
35724             this.setValue(this.el.dom.value);
35725         }
35726     },
35727
35728     /**
35729      * Returns true if this field has been changed since it was originally loaded and is not disabled.
35730      */
35731     isDirty : function() {
35732         if(this.disabled) {
35733             return false;
35734         }
35735         return String(this.getValue()) !== String(this.originalValue);
35736     },
35737
35738     // private
35739     afterRender : function(){
35740         Roo.form.Field.superclass.afterRender.call(this);
35741         this.initEvents();
35742     },
35743
35744     // private
35745     fireKey : function(e){
35746         //Roo.log('field ' + e.getKey());
35747         if(e.isNavKeyPress()){
35748             this.fireEvent("specialkey", this, e);
35749         }
35750     },
35751
35752     /**
35753      * Resets the current field value to the originally loaded value and clears any validation messages
35754      */
35755     reset : function(){
35756         this.setValue(this.originalValue);
35757         this.clearInvalid();
35758     },
35759
35760     // private
35761     initEvents : function(){
35762         // safari killled keypress - so keydown is now used..
35763         this.el.on("keydown" , this.fireKey,  this);
35764         this.el.on("focus", this.onFocus,  this);
35765         this.el.on("blur", this.onBlur,  this);
35766         this.el.relayEvent('keyup', this);
35767
35768         // reference to original value for reset
35769         this.originalValue = this.getValue();
35770     },
35771
35772     // private
35773     onFocus : function(){
35774         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
35775             this.el.addClass(this.focusClass);
35776         }
35777         if(!this.hasFocus){
35778             this.hasFocus = true;
35779             this.startValue = this.getValue();
35780             this.fireEvent("focus", this);
35781         }
35782     },
35783
35784     beforeBlur : Roo.emptyFn,
35785
35786     // private
35787     onBlur : function(){
35788         this.beforeBlur();
35789         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
35790             this.el.removeClass(this.focusClass);
35791         }
35792         this.hasFocus = false;
35793         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
35794             this.validate();
35795         }
35796         var v = this.getValue();
35797         if(String(v) !== String(this.startValue)){
35798             this.fireEvent('change', this, v, this.startValue);
35799         }
35800         this.fireEvent("blur", this);
35801     },
35802
35803     /**
35804      * Returns whether or not the field value is currently valid
35805      * @param {Boolean} preventMark True to disable marking the field invalid
35806      * @return {Boolean} True if the value is valid, else false
35807      */
35808     isValid : function(preventMark){
35809         if(this.disabled){
35810             return true;
35811         }
35812         var restore = this.preventMark;
35813         this.preventMark = preventMark === true;
35814         var v = this.validateValue(this.processValue(this.getRawValue()));
35815         this.preventMark = restore;
35816         return v;
35817     },
35818
35819     /**
35820      * Validates the field value
35821      * @return {Boolean} True if the value is valid, else false
35822      */
35823     validate : function(){
35824         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
35825             this.clearInvalid();
35826             return true;
35827         }
35828         return false;
35829     },
35830
35831     processValue : function(value){
35832         return value;
35833     },
35834
35835     // private
35836     // Subclasses should provide the validation implementation by overriding this
35837     validateValue : function(value){
35838         return true;
35839     },
35840
35841     /**
35842      * Mark this field as invalid
35843      * @param {String} msg The validation message
35844      */
35845     markInvalid : function(msg){
35846         if(!this.rendered || this.preventMark){ // not rendered
35847             return;
35848         }
35849         this.el.addClass(this.invalidClass);
35850         msg = msg || this.invalidText;
35851         switch(this.msgTarget){
35852             case 'qtip':
35853                 this.el.dom.qtip = msg;
35854                 this.el.dom.qclass = 'x-form-invalid-tip';
35855                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
35856                     Roo.QuickTips.enable();
35857                 }
35858                 break;
35859             case 'title':
35860                 this.el.dom.title = msg;
35861                 break;
35862             case 'under':
35863                 if(!this.errorEl){
35864                     var elp = this.el.findParent('.x-form-element', 5, true);
35865                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
35866                     this.errorEl.setWidth(elp.getWidth(true)-20);
35867                 }
35868                 this.errorEl.update(msg);
35869                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
35870                 break;
35871             case 'side':
35872                 if(!this.errorIcon){
35873                     var elp = this.el.findParent('.x-form-element', 5, true);
35874                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
35875                 }
35876                 this.alignErrorIcon();
35877                 this.errorIcon.dom.qtip = msg;
35878                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
35879                 this.errorIcon.show();
35880                 this.on('resize', this.alignErrorIcon, this);
35881                 break;
35882             default:
35883                 var t = Roo.getDom(this.msgTarget);
35884                 t.innerHTML = msg;
35885                 t.style.display = this.msgDisplay;
35886                 break;
35887         }
35888         this.fireEvent('invalid', this, msg);
35889     },
35890
35891     // private
35892     alignErrorIcon : function(){
35893         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
35894     },
35895
35896     /**
35897      * Clear any invalid styles/messages for this field
35898      */
35899     clearInvalid : function(){
35900         if(!this.rendered || this.preventMark){ // not rendered
35901             return;
35902         }
35903         this.el.removeClass(this.invalidClass);
35904         switch(this.msgTarget){
35905             case 'qtip':
35906                 this.el.dom.qtip = '';
35907                 break;
35908             case 'title':
35909                 this.el.dom.title = '';
35910                 break;
35911             case 'under':
35912                 if(this.errorEl){
35913                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
35914                 }
35915                 break;
35916             case 'side':
35917                 if(this.errorIcon){
35918                     this.errorIcon.dom.qtip = '';
35919                     this.errorIcon.hide();
35920                     this.un('resize', this.alignErrorIcon, this);
35921                 }
35922                 break;
35923             default:
35924                 var t = Roo.getDom(this.msgTarget);
35925                 t.innerHTML = '';
35926                 t.style.display = 'none';
35927                 break;
35928         }
35929         this.fireEvent('valid', this);
35930     },
35931
35932     /**
35933      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
35934      * @return {Mixed} value The field value
35935      */
35936     getRawValue : function(){
35937         var v = this.el.getValue();
35938         if(v === this.emptyText){
35939             v = '';
35940         }
35941         return v;
35942     },
35943
35944     /**
35945      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
35946      * @return {Mixed} value The field value
35947      */
35948     getValue : function(){
35949         var v = this.el.getValue();
35950         if(v === this.emptyText || v === undefined){
35951             v = '';
35952         }
35953         return v;
35954     },
35955
35956     /**
35957      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
35958      * @param {Mixed} value The value to set
35959      */
35960     setRawValue : function(v){
35961         return this.el.dom.value = (v === null || v === undefined ? '' : v);
35962     },
35963
35964     /**
35965      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
35966      * @param {Mixed} value The value to set
35967      */
35968     setValue : function(v){
35969         this.value = v;
35970         if(this.rendered){
35971             this.el.dom.value = (v === null || v === undefined ? '' : v);
35972              this.validate();
35973         }
35974     },
35975
35976     adjustSize : function(w, h){
35977         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
35978         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
35979         return s;
35980     },
35981
35982     adjustWidth : function(tag, w){
35983         tag = tag.toLowerCase();
35984         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
35985             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
35986                 if(tag == 'input'){
35987                     return w + 2;
35988                 }
35989                 if(tag = 'textarea'){
35990                     return w-2;
35991                 }
35992             }else if(Roo.isOpera){
35993                 if(tag == 'input'){
35994                     return w + 2;
35995                 }
35996                 if(tag = 'textarea'){
35997                     return w-2;
35998                 }
35999             }
36000         }
36001         return w;
36002     }
36003 });
36004
36005
36006 // anything other than normal should be considered experimental
36007 Roo.form.Field.msgFx = {
36008     normal : {
36009         show: function(msgEl, f){
36010             msgEl.setDisplayed('block');
36011         },
36012
36013         hide : function(msgEl, f){
36014             msgEl.setDisplayed(false).update('');
36015         }
36016     },
36017
36018     slide : {
36019         show: function(msgEl, f){
36020             msgEl.slideIn('t', {stopFx:true});
36021         },
36022
36023         hide : function(msgEl, f){
36024             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
36025         }
36026     },
36027
36028     slideRight : {
36029         show: function(msgEl, f){
36030             msgEl.fixDisplay();
36031             msgEl.alignTo(f.el, 'tl-tr');
36032             msgEl.slideIn('l', {stopFx:true});
36033         },
36034
36035         hide : function(msgEl, f){
36036             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
36037         }
36038     }
36039 };/*
36040  * Based on:
36041  * Ext JS Library 1.1.1
36042  * Copyright(c) 2006-2007, Ext JS, LLC.
36043  *
36044  * Originally Released Under LGPL - original licence link has changed is not relivant.
36045  *
36046  * Fork - LGPL
36047  * <script type="text/javascript">
36048  */
36049  
36050
36051 /**
36052  * @class Roo.form.TextField
36053  * @extends Roo.form.Field
36054  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
36055  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
36056  * @constructor
36057  * Creates a new TextField
36058  * @param {Object} config Configuration options
36059  */
36060 Roo.form.TextField = function(config){
36061     Roo.form.TextField.superclass.constructor.call(this, config);
36062     this.addEvents({
36063         /**
36064          * @event autosize
36065          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
36066          * according to the default logic, but this event provides a hook for the developer to apply additional
36067          * logic at runtime to resize the field if needed.
36068              * @param {Roo.form.Field} this This text field
36069              * @param {Number} width The new field width
36070              */
36071         autosize : true
36072     });
36073 };
36074
36075 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
36076     /**
36077      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
36078      */
36079     grow : false,
36080     /**
36081      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
36082      */
36083     growMin : 30,
36084     /**
36085      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
36086      */
36087     growMax : 800,
36088     /**
36089      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
36090      */
36091     vtype : null,
36092     /**
36093      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
36094      */
36095     maskRe : null,
36096     /**
36097      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
36098      */
36099     disableKeyFilter : false,
36100     /**
36101      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
36102      */
36103     allowBlank : true,
36104     /**
36105      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
36106      */
36107     minLength : 0,
36108     /**
36109      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
36110      */
36111     maxLength : Number.MAX_VALUE,
36112     /**
36113      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
36114      */
36115     minLengthText : "The minimum length for this field is {0}",
36116     /**
36117      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
36118      */
36119     maxLengthText : "The maximum length for this field is {0}",
36120     /**
36121      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
36122      */
36123     selectOnFocus : false,
36124     /**
36125      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
36126      */
36127     blankText : "This field is required",
36128     /**
36129      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
36130      * If available, this function will be called only after the basic validators all return true, and will be passed the
36131      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
36132      */
36133     validator : null,
36134     /**
36135      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
36136      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
36137      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
36138      */
36139     regex : null,
36140     /**
36141      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
36142      */
36143     regexText : "",
36144     /**
36145      * @cfg {String} emptyText The default text to display in an empty field (defaults to null).
36146      */
36147     emptyText : null,
36148     /**
36149      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the {@link #emptyText} (defaults to
36150      * 'x-form-empty-field').  This class is automatically added and removed as needed depending on the current field value.
36151      */
36152     emptyClass : 'x-form-empty-field',
36153
36154     // private
36155     initEvents : function(){
36156         Roo.form.TextField.superclass.initEvents.call(this);
36157         if(this.validationEvent == 'keyup'){
36158             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
36159             this.el.on('keyup', this.filterValidation, this);
36160         }
36161         else if(this.validationEvent !== false){
36162             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
36163         }
36164         if(this.selectOnFocus || this.emptyText){
36165             this.on("focus", this.preFocus, this);
36166             if(this.emptyText){
36167                 this.on('blur', this.postBlur, this);
36168                 this.applyEmptyText();
36169             }
36170         }
36171         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
36172             this.el.on("keypress", this.filterKeys, this);
36173         }
36174         if(this.grow){
36175             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
36176             this.el.on("click", this.autoSize,  this);
36177         }
36178     },
36179
36180     processValue : function(value){
36181         if(this.stripCharsRe){
36182             var newValue = value.replace(this.stripCharsRe, '');
36183             if(newValue !== value){
36184                 this.setRawValue(newValue);
36185                 return newValue;
36186             }
36187         }
36188         return value;
36189     },
36190
36191     filterValidation : function(e){
36192         if(!e.isNavKeyPress()){
36193             this.validationTask.delay(this.validationDelay);
36194         }
36195     },
36196
36197     // private
36198     onKeyUp : function(e){
36199         if(!e.isNavKeyPress()){
36200             this.autoSize();
36201         }
36202     },
36203
36204     /**
36205      * Resets the current field value to the originally-loaded value and clears any validation messages.
36206      * Also adds emptyText and emptyClass if the original value was blank.
36207      */
36208     reset : function(){
36209         Roo.form.TextField.superclass.reset.call(this);
36210         this.applyEmptyText();
36211     },
36212
36213     applyEmptyText : function(){
36214         if(this.rendered && this.emptyText && this.getRawValue().length < 1){
36215             this.setRawValue(this.emptyText);
36216             this.el.addClass(this.emptyClass);
36217         }
36218     },
36219
36220     // private
36221     preFocus : function(){
36222         if(this.emptyText){
36223             if(this.el.dom.value == this.emptyText){
36224                 this.setRawValue('');
36225             }
36226             this.el.removeClass(this.emptyClass);
36227         }
36228         if(this.selectOnFocus){
36229             this.el.dom.select();
36230         }
36231     },
36232
36233     // private
36234     postBlur : function(){
36235         this.applyEmptyText();
36236     },
36237
36238     // private
36239     filterKeys : function(e){
36240         var k = e.getKey();
36241         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
36242             return;
36243         }
36244         var c = e.getCharCode(), cc = String.fromCharCode(c);
36245         if(Roo.isIE && (e.isSpecialKey() || !cc)){
36246             return;
36247         }
36248         if(!this.maskRe.test(cc)){
36249             e.stopEvent();
36250         }
36251     },
36252
36253     setValue : function(v){
36254         if(this.emptyText && this.el && v !== undefined && v !== null && v !== ''){
36255             this.el.removeClass(this.emptyClass);
36256         }
36257         Roo.form.TextField.superclass.setValue.apply(this, arguments);
36258         this.applyEmptyText();
36259         this.autoSize();
36260     },
36261
36262     /**
36263      * Validates a value according to the field's validation rules and marks the field as invalid
36264      * if the validation fails
36265      * @param {Mixed} value The value to validate
36266      * @return {Boolean} True if the value is valid, else false
36267      */
36268     validateValue : function(value){
36269         if(value.length < 1 || value === this.emptyText){ // if it's blank
36270              if(this.allowBlank){
36271                 this.clearInvalid();
36272                 return true;
36273              }else{
36274                 this.markInvalid(this.blankText);
36275                 return false;
36276              }
36277         }
36278         if(value.length < this.minLength){
36279             this.markInvalid(String.format(this.minLengthText, this.minLength));
36280             return false;
36281         }
36282         if(value.length > this.maxLength){
36283             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
36284             return false;
36285         }
36286         if(this.vtype){
36287             var vt = Roo.form.VTypes;
36288             if(!vt[this.vtype](value, this)){
36289                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
36290                 return false;
36291             }
36292         }
36293         if(typeof this.validator == "function"){
36294             var msg = this.validator(value);
36295             if(msg !== true){
36296                 this.markInvalid(msg);
36297                 return false;
36298             }
36299         }
36300         if(this.regex && !this.regex.test(value)){
36301             this.markInvalid(this.regexText);
36302             return false;
36303         }
36304         return true;
36305     },
36306
36307     /**
36308      * Selects text in this field
36309      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
36310      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
36311      */
36312     selectText : function(start, end){
36313         var v = this.getRawValue();
36314         if(v.length > 0){
36315             start = start === undefined ? 0 : start;
36316             end = end === undefined ? v.length : end;
36317             var d = this.el.dom;
36318             if(d.setSelectionRange){
36319                 d.setSelectionRange(start, end);
36320             }else if(d.createTextRange){
36321                 var range = d.createTextRange();
36322                 range.moveStart("character", start);
36323                 range.moveEnd("character", v.length-end);
36324                 range.select();
36325             }
36326         }
36327     },
36328
36329     /**
36330      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
36331      * This only takes effect if grow = true, and fires the autosize event.
36332      */
36333     autoSize : function(){
36334         if(!this.grow || !this.rendered){
36335             return;
36336         }
36337         if(!this.metrics){
36338             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
36339         }
36340         var el = this.el;
36341         var v = el.dom.value;
36342         var d = document.createElement('div');
36343         d.appendChild(document.createTextNode(v));
36344         v = d.innerHTML;
36345         d = null;
36346         v += "&#160;";
36347         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
36348         this.el.setWidth(w);
36349         this.fireEvent("autosize", this, w);
36350     }
36351 });/*
36352  * Based on:
36353  * Ext JS Library 1.1.1
36354  * Copyright(c) 2006-2007, Ext JS, LLC.
36355  *
36356  * Originally Released Under LGPL - original licence link has changed is not relivant.
36357  *
36358  * Fork - LGPL
36359  * <script type="text/javascript">
36360  */
36361  
36362 /**
36363  * @class Roo.form.Hidden
36364  * @extends Roo.form.TextField
36365  * Simple Hidden element used on forms 
36366  * 
36367  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
36368  * 
36369  * @constructor
36370  * Creates a new Hidden form element.
36371  * @param {Object} config Configuration options
36372  */
36373
36374
36375
36376 // easy hidden field...
36377 Roo.form.Hidden = function(config){
36378     Roo.form.Hidden.superclass.constructor.call(this, config);
36379 };
36380   
36381 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
36382     fieldLabel:      '',
36383     inputType:      'hidden',
36384     width:          50,
36385     allowBlank:     true,
36386     labelSeparator: '',
36387     hidden:         true,
36388     itemCls :       'x-form-item-display-none'
36389
36390
36391 });
36392
36393
36394 /*
36395  * Based on:
36396  * Ext JS Library 1.1.1
36397  * Copyright(c) 2006-2007, Ext JS, LLC.
36398  *
36399  * Originally Released Under LGPL - original licence link has changed is not relivant.
36400  *
36401  * Fork - LGPL
36402  * <script type="text/javascript">
36403  */
36404  
36405 /**
36406  * @class Roo.form.TriggerField
36407  * @extends Roo.form.TextField
36408  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
36409  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
36410  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
36411  * for which you can provide a custom implementation.  For example:
36412  * <pre><code>
36413 var trigger = new Roo.form.TriggerField();
36414 trigger.onTriggerClick = myTriggerFn;
36415 trigger.applyTo('my-field');
36416 </code></pre>
36417  *
36418  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
36419  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
36420  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
36421  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
36422  * @constructor
36423  * Create a new TriggerField.
36424  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
36425  * to the base TextField)
36426  */
36427 Roo.form.TriggerField = function(config){
36428     this.mimicing = false;
36429     Roo.form.TriggerField.superclass.constructor.call(this, config);
36430 };
36431
36432 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
36433     /**
36434      * @cfg {String} triggerClass A CSS class to apply to the trigger
36435      */
36436     /**
36437      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36438      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
36439      */
36440     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
36441     /**
36442      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
36443      */
36444     hideTrigger:false,
36445
36446     /** @cfg {Boolean} grow @hide */
36447     /** @cfg {Number} growMin @hide */
36448     /** @cfg {Number} growMax @hide */
36449
36450     /**
36451      * @hide 
36452      * @method
36453      */
36454     autoSize: Roo.emptyFn,
36455     // private
36456     monitorTab : true,
36457     // private
36458     deferHeight : true,
36459
36460     
36461     actionMode : 'wrap',
36462     // private
36463     onResize : function(w, h){
36464         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
36465         if(typeof w == 'number'){
36466             var x = w - this.trigger.getWidth();
36467             this.el.setWidth(this.adjustWidth('input', x));
36468             this.trigger.setStyle('left', x+'px');
36469         }
36470     },
36471
36472     // private
36473     adjustSize : Roo.BoxComponent.prototype.adjustSize,
36474
36475     // private
36476     getResizeEl : function(){
36477         return this.wrap;
36478     },
36479
36480     // private
36481     getPositionEl : function(){
36482         return this.wrap;
36483     },
36484
36485     // private
36486     alignErrorIcon : function(){
36487         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
36488     },
36489
36490     // private
36491     onRender : function(ct, position){
36492         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
36493         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
36494         this.trigger = this.wrap.createChild(this.triggerConfig ||
36495                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
36496         if(this.hideTrigger){
36497             this.trigger.setDisplayed(false);
36498         }
36499         this.initTrigger();
36500         if(!this.width){
36501             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
36502         }
36503     },
36504
36505     // private
36506     initTrigger : function(){
36507         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
36508         this.trigger.addClassOnOver('x-form-trigger-over');
36509         this.trigger.addClassOnClick('x-form-trigger-click');
36510     },
36511
36512     // private
36513     onDestroy : function(){
36514         if(this.trigger){
36515             this.trigger.removeAllListeners();
36516             this.trigger.remove();
36517         }
36518         if(this.wrap){
36519             this.wrap.remove();
36520         }
36521         Roo.form.TriggerField.superclass.onDestroy.call(this);
36522     },
36523
36524     // private
36525     onFocus : function(){
36526         Roo.form.TriggerField.superclass.onFocus.call(this);
36527         if(!this.mimicing){
36528             this.wrap.addClass('x-trigger-wrap-focus');
36529             this.mimicing = true;
36530             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
36531             if(this.monitorTab){
36532                 this.el.on("keydown", this.checkTab, this);
36533             }
36534         }
36535     },
36536
36537     // private
36538     checkTab : function(e){
36539         if(e.getKey() == e.TAB){
36540             this.triggerBlur();
36541         }
36542     },
36543
36544     // private
36545     onBlur : function(){
36546         // do nothing
36547     },
36548
36549     // private
36550     mimicBlur : function(e, t){
36551         if(!this.wrap.contains(t) && this.validateBlur()){
36552             this.triggerBlur();
36553         }
36554     },
36555
36556     // private
36557     triggerBlur : function(){
36558         this.mimicing = false;
36559         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
36560         if(this.monitorTab){
36561             this.el.un("keydown", this.checkTab, this);
36562         }
36563         this.wrap.removeClass('x-trigger-wrap-focus');
36564         Roo.form.TriggerField.superclass.onBlur.call(this);
36565     },
36566
36567     // private
36568     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
36569     validateBlur : function(e, t){
36570         return true;
36571     },
36572
36573     // private
36574     onDisable : function(){
36575         Roo.form.TriggerField.superclass.onDisable.call(this);
36576         if(this.wrap){
36577             this.wrap.addClass('x-item-disabled');
36578         }
36579     },
36580
36581     // private
36582     onEnable : function(){
36583         Roo.form.TriggerField.superclass.onEnable.call(this);
36584         if(this.wrap){
36585             this.wrap.removeClass('x-item-disabled');
36586         }
36587     },
36588
36589     // private
36590     onShow : function(){
36591         var ae = this.getActionEl();
36592         
36593         if(ae){
36594             ae.dom.style.display = '';
36595             ae.dom.style.visibility = 'visible';
36596         }
36597     },
36598
36599     // private
36600     
36601     onHide : function(){
36602         var ae = this.getActionEl();
36603         ae.dom.style.display = 'none';
36604     },
36605
36606     /**
36607      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
36608      * by an implementing function.
36609      * @method
36610      * @param {EventObject} e
36611      */
36612     onTriggerClick : Roo.emptyFn
36613 });
36614
36615 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
36616 // to be extended by an implementing class.  For an example of implementing this class, see the custom
36617 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
36618 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
36619     initComponent : function(){
36620         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
36621
36622         this.triggerConfig = {
36623             tag:'span', cls:'x-form-twin-triggers', cn:[
36624             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
36625             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
36626         ]};
36627     },
36628
36629     getTrigger : function(index){
36630         return this.triggers[index];
36631     },
36632
36633     initTrigger : function(){
36634         var ts = this.trigger.select('.x-form-trigger', true);
36635         this.wrap.setStyle('overflow', 'hidden');
36636         var triggerField = this;
36637         ts.each(function(t, all, index){
36638             t.hide = function(){
36639                 var w = triggerField.wrap.getWidth();
36640                 this.dom.style.display = 'none';
36641                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
36642             };
36643             t.show = function(){
36644                 var w = triggerField.wrap.getWidth();
36645                 this.dom.style.display = '';
36646                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
36647             };
36648             var triggerIndex = 'Trigger'+(index+1);
36649
36650             if(this['hide'+triggerIndex]){
36651                 t.dom.style.display = 'none';
36652             }
36653             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
36654             t.addClassOnOver('x-form-trigger-over');
36655             t.addClassOnClick('x-form-trigger-click');
36656         }, this);
36657         this.triggers = ts.elements;
36658     },
36659
36660     onTrigger1Click : Roo.emptyFn,
36661     onTrigger2Click : Roo.emptyFn
36662 });/*
36663  * Based on:
36664  * Ext JS Library 1.1.1
36665  * Copyright(c) 2006-2007, Ext JS, LLC.
36666  *
36667  * Originally Released Under LGPL - original licence link has changed is not relivant.
36668  *
36669  * Fork - LGPL
36670  * <script type="text/javascript">
36671  */
36672  
36673 /**
36674  * @class Roo.form.TextArea
36675  * @extends Roo.form.TextField
36676  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
36677  * support for auto-sizing.
36678  * @constructor
36679  * Creates a new TextArea
36680  * @param {Object} config Configuration options
36681  */
36682 Roo.form.TextArea = function(config){
36683     Roo.form.TextArea.superclass.constructor.call(this, config);
36684     // these are provided exchanges for backwards compat
36685     // minHeight/maxHeight were replaced by growMin/growMax to be
36686     // compatible with TextField growing config values
36687     if(this.minHeight !== undefined){
36688         this.growMin = this.minHeight;
36689     }
36690     if(this.maxHeight !== undefined){
36691         this.growMax = this.maxHeight;
36692     }
36693 };
36694
36695 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
36696     /**
36697      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
36698      */
36699     growMin : 60,
36700     /**
36701      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
36702      */
36703     growMax: 1000,
36704     /**
36705      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
36706      * in the field (equivalent to setting overflow: hidden, defaults to false)
36707      */
36708     preventScrollbars: false,
36709     /**
36710      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36711      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
36712      */
36713
36714     // private
36715     onRender : function(ct, position){
36716         if(!this.el){
36717             this.defaultAutoCreate = {
36718                 tag: "textarea",
36719                 style:"width:300px;height:60px;",
36720                 autocomplete: "off"
36721             };
36722         }
36723         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
36724         if(this.grow){
36725             this.textSizeEl = Roo.DomHelper.append(document.body, {
36726                 tag: "pre", cls: "x-form-grow-sizer"
36727             });
36728             if(this.preventScrollbars){
36729                 this.el.setStyle("overflow", "hidden");
36730             }
36731             this.el.setHeight(this.growMin);
36732         }
36733     },
36734
36735     onDestroy : function(){
36736         if(this.textSizeEl){
36737             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
36738         }
36739         Roo.form.TextArea.superclass.onDestroy.call(this);
36740     },
36741
36742     // private
36743     onKeyUp : function(e){
36744         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
36745             this.autoSize();
36746         }
36747     },
36748
36749     /**
36750      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
36751      * This only takes effect if grow = true, and fires the autosize event if the height changes.
36752      */
36753     autoSize : function(){
36754         if(!this.grow || !this.textSizeEl){
36755             return;
36756         }
36757         var el = this.el;
36758         var v = el.dom.value;
36759         var ts = this.textSizeEl;
36760
36761         ts.innerHTML = '';
36762         ts.appendChild(document.createTextNode(v));
36763         v = ts.innerHTML;
36764
36765         Roo.fly(ts).setWidth(this.el.getWidth());
36766         if(v.length < 1){
36767             v = "&#160;&#160;";
36768         }else{
36769             if(Roo.isIE){
36770                 v = v.replace(/\n/g, '<p>&#160;</p>');
36771             }
36772             v += "&#160;\n&#160;";
36773         }
36774         ts.innerHTML = v;
36775         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
36776         if(h != this.lastHeight){
36777             this.lastHeight = h;
36778             this.el.setHeight(h);
36779             this.fireEvent("autosize", this, h);
36780         }
36781     }
36782 });/*
36783  * Based on:
36784  * Ext JS Library 1.1.1
36785  * Copyright(c) 2006-2007, Ext JS, LLC.
36786  *
36787  * Originally Released Under LGPL - original licence link has changed is not relivant.
36788  *
36789  * Fork - LGPL
36790  * <script type="text/javascript">
36791  */
36792  
36793
36794 /**
36795  * @class Roo.form.NumberField
36796  * @extends Roo.form.TextField
36797  * Numeric text field that provides automatic keystroke filtering and numeric validation.
36798  * @constructor
36799  * Creates a new NumberField
36800  * @param {Object} config Configuration options
36801  */
36802 Roo.form.NumberField = function(config){
36803     Roo.form.NumberField.superclass.constructor.call(this, config);
36804 };
36805
36806 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
36807     /**
36808      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
36809      */
36810     fieldClass: "x-form-field x-form-num-field",
36811     /**
36812      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36813      */
36814     allowDecimals : true,
36815     /**
36816      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36817      */
36818     decimalSeparator : ".",
36819     /**
36820      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36821      */
36822     decimalPrecision : 2,
36823     /**
36824      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36825      */
36826     allowNegative : true,
36827     /**
36828      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36829      */
36830     minValue : Number.NEGATIVE_INFINITY,
36831     /**
36832      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36833      */
36834     maxValue : Number.MAX_VALUE,
36835     /**
36836      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36837      */
36838     minText : "The minimum value for this field is {0}",
36839     /**
36840      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36841      */
36842     maxText : "The maximum value for this field is {0}",
36843     /**
36844      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36845      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36846      */
36847     nanText : "{0} is not a valid number",
36848
36849     // private
36850     initEvents : function(){
36851         Roo.form.NumberField.superclass.initEvents.call(this);
36852         var allowed = "0123456789";
36853         if(this.allowDecimals){
36854             allowed += this.decimalSeparator;
36855         }
36856         if(this.allowNegative){
36857             allowed += "-";
36858         }
36859         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36860         var keyPress = function(e){
36861             var k = e.getKey();
36862             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36863                 return;
36864             }
36865             var c = e.getCharCode();
36866             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36867                 e.stopEvent();
36868             }
36869         };
36870         this.el.on("keypress", keyPress, this);
36871     },
36872
36873     // private
36874     validateValue : function(value){
36875         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
36876             return false;
36877         }
36878         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
36879              return true;
36880         }
36881         var num = this.parseValue(value);
36882         if(isNaN(num)){
36883             this.markInvalid(String.format(this.nanText, value));
36884             return false;
36885         }
36886         if(num < this.minValue){
36887             this.markInvalid(String.format(this.minText, this.minValue));
36888             return false;
36889         }
36890         if(num > this.maxValue){
36891             this.markInvalid(String.format(this.maxText, this.maxValue));
36892             return false;
36893         }
36894         return true;
36895     },
36896
36897     getValue : function(){
36898         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
36899     },
36900
36901     // private
36902     parseValue : function(value){
36903         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36904         return isNaN(value) ? '' : value;
36905     },
36906
36907     // private
36908     fixPrecision : function(value){
36909         var nan = isNaN(value);
36910         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36911             return nan ? '' : value;
36912         }
36913         return parseFloat(value).toFixed(this.decimalPrecision);
36914     },
36915
36916     setValue : function(v){
36917         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
36918     },
36919
36920     // private
36921     decimalPrecisionFcn : function(v){
36922         return Math.floor(v);
36923     },
36924
36925     beforeBlur : function(){
36926         var v = this.parseValue(this.getRawValue());
36927         if(v){
36928             this.setValue(this.fixPrecision(v));
36929         }
36930     }
36931 });/*
36932  * Based on:
36933  * Ext JS Library 1.1.1
36934  * Copyright(c) 2006-2007, Ext JS, LLC.
36935  *
36936  * Originally Released Under LGPL - original licence link has changed is not relivant.
36937  *
36938  * Fork - LGPL
36939  * <script type="text/javascript">
36940  */
36941  
36942 /**
36943  * @class Roo.form.DateField
36944  * @extends Roo.form.TriggerField
36945  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
36946 * @constructor
36947 * Create a new DateField
36948 * @param {Object} config
36949  */
36950 Roo.form.DateField = function(config){
36951     Roo.form.DateField.superclass.constructor.call(this, config);
36952     
36953       this.addEvents({
36954          
36955         /**
36956          * @event select
36957          * Fires when a date is selected
36958              * @param {Roo.form.DateField} combo This combo box
36959              * @param {Date} date The date selected
36960              */
36961         'select' : true
36962          
36963     });
36964     
36965     
36966     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
36967     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
36968     this.ddMatch = null;
36969     if(this.disabledDates){
36970         var dd = this.disabledDates;
36971         var re = "(?:";
36972         for(var i = 0; i < dd.length; i++){
36973             re += dd[i];
36974             if(i != dd.length-1) re += "|";
36975         }
36976         this.ddMatch = new RegExp(re + ")");
36977     }
36978 };
36979
36980 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
36981     /**
36982      * @cfg {String} format
36983      * The default date format string which can be overriden for localization support.  The format must be
36984      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
36985      */
36986     format : "m/d/y",
36987     /**
36988      * @cfg {String} altFormats
36989      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
36990      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
36991      */
36992     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
36993     /**
36994      * @cfg {Array} disabledDays
36995      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
36996      */
36997     disabledDays : null,
36998     /**
36999      * @cfg {String} disabledDaysText
37000      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
37001      */
37002     disabledDaysText : "Disabled",
37003     /**
37004      * @cfg {Array} disabledDates
37005      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
37006      * expression so they are very powerful. Some examples:
37007      * <ul>
37008      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
37009      * <li>["03/08", "09/16"] would disable those days for every year</li>
37010      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
37011      * <li>["03/../2006"] would disable every day in March 2006</li>
37012      * <li>["^03"] would disable every day in every March</li>
37013      * </ul>
37014      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
37015      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
37016      */
37017     disabledDates : null,
37018     /**
37019      * @cfg {String} disabledDatesText
37020      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
37021      */
37022     disabledDatesText : "Disabled",
37023     /**
37024      * @cfg {Date/String} minValue
37025      * The minimum allowed date. Can be either a Javascript date object or a string date in a
37026      * valid format (defaults to null).
37027      */
37028     minValue : null,
37029     /**
37030      * @cfg {Date/String} maxValue
37031      * The maximum allowed date. Can be either a Javascript date object or a string date in a
37032      * valid format (defaults to null).
37033      */
37034     maxValue : null,
37035     /**
37036      * @cfg {String} minText
37037      * The error text to display when the date in the cell is before minValue (defaults to
37038      * 'The date in this field must be after {minValue}').
37039      */
37040     minText : "The date in this field must be equal to or after {0}",
37041     /**
37042      * @cfg {String} maxText
37043      * The error text to display when the date in the cell is after maxValue (defaults to
37044      * 'The date in this field must be before {maxValue}').
37045      */
37046     maxText : "The date in this field must be equal to or before {0}",
37047     /**
37048      * @cfg {String} invalidText
37049      * The error text to display when the date in the field is invalid (defaults to
37050      * '{value} is not a valid date - it must be in the format {format}').
37051      */
37052     invalidText : "{0} is not a valid date - it must be in the format {1}",
37053     /**
37054      * @cfg {String} triggerClass
37055      * An additional CSS class used to style the trigger button.  The trigger will always get the
37056      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
37057      * which displays a calendar icon).
37058      */
37059     triggerClass : 'x-form-date-trigger',
37060     
37061
37062     /**
37063      * @cfg {bool} useIso
37064      * if enabled, then the date field will use a hidden field to store the 
37065      * real value as iso formated date. default (false)
37066      */ 
37067     useIso : false,
37068     /**
37069      * @cfg {String/Object} autoCreate
37070      * A DomHelper element spec, or true for a default element spec (defaults to
37071      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
37072      */ 
37073     // private
37074     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
37075     
37076     // private
37077     hiddenField: false,
37078     
37079     onRender : function(ct, position)
37080     {
37081         Roo.form.DateField.superclass.onRender.call(this, ct, position);
37082         if (this.useIso) {
37083             this.el.dom.removeAttribute('name'); 
37084             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
37085                     'before', true);
37086             this.hiddenField.value = this.formatDate(this.value, 'Y-m-d');
37087             // prevent input submission
37088             this.hiddenName = this.name;
37089         }
37090             
37091             
37092     },
37093     
37094     // private
37095     validateValue : function(value)
37096     {
37097         value = this.formatDate(value);
37098         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
37099             return false;
37100         }
37101         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
37102              return true;
37103         }
37104         var svalue = value;
37105         value = this.parseDate(value);
37106         if(!value){
37107             this.markInvalid(String.format(this.invalidText, svalue, this.format));
37108             return false;
37109         }
37110         var time = value.getTime();
37111         if(this.minValue && time < this.minValue.getTime()){
37112             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
37113             return false;
37114         }
37115         if(this.maxValue && time > this.maxValue.getTime()){
37116             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
37117             return false;
37118         }
37119         if(this.disabledDays){
37120             var day = value.getDay();
37121             for(var i = 0; i < this.disabledDays.length; i++) {
37122                 if(day === this.disabledDays[i]){
37123                     this.markInvalid(this.disabledDaysText);
37124                     return false;
37125                 }
37126             }
37127         }
37128         var fvalue = this.formatDate(value);
37129         if(this.ddMatch && this.ddMatch.test(fvalue)){
37130             this.markInvalid(String.format(this.disabledDatesText, fvalue));
37131             return false;
37132         }
37133         return true;
37134     },
37135
37136     // private
37137     // Provides logic to override the default TriggerField.validateBlur which just returns true
37138     validateBlur : function(){
37139         return !this.menu || !this.menu.isVisible();
37140     },
37141
37142     /**
37143      * Returns the current date value of the date field.
37144      * @return {Date} The date value
37145      */
37146     getValue : function(){
37147         
37148         return  this.hiddenField ? this.hiddenField.value : this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
37149     },
37150
37151     /**
37152      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
37153      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
37154      * (the default format used is "m/d/y").
37155      * <br />Usage:
37156      * <pre><code>
37157 //All of these calls set the same date value (May 4, 2006)
37158
37159 //Pass a date object:
37160 var dt = new Date('5/4/06');
37161 dateField.setValue(dt);
37162
37163 //Pass a date string (default format):
37164 dateField.setValue('5/4/06');
37165
37166 //Pass a date string (custom format):
37167 dateField.format = 'Y-m-d';
37168 dateField.setValue('2006-5-4');
37169 </code></pre>
37170      * @param {String/Date} date The date or valid date string
37171      */
37172     setValue : function(date){
37173         if (this.hiddenField) {
37174             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
37175         }
37176         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
37177     },
37178
37179     // private
37180     parseDate : function(value){
37181         if(!value || value instanceof Date){
37182             return value;
37183         }
37184         var v = Date.parseDate(value, this.format);
37185         if(!v && this.altFormats){
37186             if(!this.altFormatsArray){
37187                 this.altFormatsArray = this.altFormats.split("|");
37188             }
37189             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
37190                 v = Date.parseDate(value, this.altFormatsArray[i]);
37191             }
37192         }
37193         return v;
37194     },
37195
37196     // private
37197     formatDate : function(date, fmt){
37198         return (!date || !(date instanceof Date)) ?
37199                date : date.dateFormat(fmt || this.format);
37200     },
37201
37202     // private
37203     menuListeners : {
37204         select: function(m, d){
37205             this.setValue(d);
37206             this.fireEvent('select', this, d);
37207         },
37208         show : function(){ // retain focus styling
37209             this.onFocus();
37210         },
37211         hide : function(){
37212             this.focus.defer(10, this);
37213             var ml = this.menuListeners;
37214             this.menu.un("select", ml.select,  this);
37215             this.menu.un("show", ml.show,  this);
37216             this.menu.un("hide", ml.hide,  this);
37217         }
37218     },
37219
37220     // private
37221     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
37222     onTriggerClick : function(){
37223         if(this.disabled){
37224             return;
37225         }
37226         if(this.menu == null){
37227             this.menu = new Roo.menu.DateMenu();
37228         }
37229         Roo.apply(this.menu.picker,  {
37230             showClear: this.allowBlank,
37231             minDate : this.minValue,
37232             maxDate : this.maxValue,
37233             disabledDatesRE : this.ddMatch,
37234             disabledDatesText : this.disabledDatesText,
37235             disabledDays : this.disabledDays,
37236             disabledDaysText : this.disabledDaysText,
37237             format : this.format,
37238             minText : String.format(this.minText, this.formatDate(this.minValue)),
37239             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
37240         });
37241         this.menu.on(Roo.apply({}, this.menuListeners, {
37242             scope:this
37243         }));
37244         this.menu.picker.setValue(this.getValue() || new Date());
37245         this.menu.show(this.el, "tl-bl?");
37246     },
37247
37248     beforeBlur : function(){
37249         var v = this.parseDate(this.getRawValue());
37250         if(v){
37251             this.setValue(v);
37252         }
37253     }
37254
37255     /** @cfg {Boolean} grow @hide */
37256     /** @cfg {Number} growMin @hide */
37257     /** @cfg {Number} growMax @hide */
37258     /**
37259      * @hide
37260      * @method autoSize
37261      */
37262 });/*
37263  * Based on:
37264  * Ext JS Library 1.1.1
37265  * Copyright(c) 2006-2007, Ext JS, LLC.
37266  *
37267  * Originally Released Under LGPL - original licence link has changed is not relivant.
37268  *
37269  * Fork - LGPL
37270  * <script type="text/javascript">
37271  */
37272  
37273
37274 /**
37275  * @class Roo.form.ComboBox
37276  * @extends Roo.form.TriggerField
37277  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
37278  * @constructor
37279  * Create a new ComboBox.
37280  * @param {Object} config Configuration options
37281  */
37282 Roo.form.ComboBox = function(config){
37283     Roo.form.ComboBox.superclass.constructor.call(this, config);
37284     this.addEvents({
37285         /**
37286          * @event expand
37287          * Fires when the dropdown list is expanded
37288              * @param {Roo.form.ComboBox} combo This combo box
37289              */
37290         'expand' : true,
37291         /**
37292          * @event collapse
37293          * Fires when the dropdown list is collapsed
37294              * @param {Roo.form.ComboBox} combo This combo box
37295              */
37296         'collapse' : true,
37297         /**
37298          * @event beforeselect
37299          * Fires before a list item is selected. Return false to cancel the selection.
37300              * @param {Roo.form.ComboBox} combo This combo box
37301              * @param {Roo.data.Record} record The data record returned from the underlying store
37302              * @param {Number} index The index of the selected item in the dropdown list
37303              */
37304         'beforeselect' : true,
37305         /**
37306          * @event select
37307          * Fires when a list item is selected
37308              * @param {Roo.form.ComboBox} combo This combo box
37309              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
37310              * @param {Number} index The index of the selected item in the dropdown list
37311              */
37312         'select' : true,
37313         /**
37314          * @event beforequery
37315          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
37316          * The event object passed has these properties:
37317              * @param {Roo.form.ComboBox} combo This combo box
37318              * @param {String} query The query
37319              * @param {Boolean} forceAll true to force "all" query
37320              * @param {Boolean} cancel true to cancel the query
37321              * @param {Object} e The query event object
37322              */
37323         'beforequery': true,
37324          /**
37325          * @event add
37326          * Fires when the 'add' icon is pressed (add a listener to enable add button)
37327              * @param {Roo.form.ComboBox} combo This combo box
37328              */
37329         'add' : true,
37330         /**
37331          * @event edit
37332          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
37333              * @param {Roo.form.ComboBox} combo This combo box
37334              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
37335              */
37336         'edit' : true
37337         
37338         
37339     });
37340     if(this.transform){
37341         this.allowDomMove = false;
37342         var s = Roo.getDom(this.transform);
37343         if(!this.hiddenName){
37344             this.hiddenName = s.name;
37345         }
37346         if(!this.store){
37347             this.mode = 'local';
37348             var d = [], opts = s.options;
37349             for(var i = 0, len = opts.length;i < len; i++){
37350                 var o = opts[i];
37351                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
37352                 if(o.selected) {
37353                     this.value = value;
37354                 }
37355                 d.push([value, o.text]);
37356             }
37357             this.store = new Roo.data.SimpleStore({
37358                 'id': 0,
37359                 fields: ['value', 'text'],
37360                 data : d
37361             });
37362             this.valueField = 'value';
37363             this.displayField = 'text';
37364         }
37365         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
37366         if(!this.lazyRender){
37367             this.target = true;
37368             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
37369             s.parentNode.removeChild(s); // remove it
37370             this.render(this.el.parentNode);
37371         }else{
37372             s.parentNode.removeChild(s); // remove it
37373         }
37374
37375     }
37376     if (this.store) {
37377         this.store = Roo.factory(this.store, Roo.data);
37378     }
37379     
37380     this.selectedIndex = -1;
37381     if(this.mode == 'local'){
37382         if(config.queryDelay === undefined){
37383             this.queryDelay = 10;
37384         }
37385         if(config.minChars === undefined){
37386             this.minChars = 0;
37387         }
37388     }
37389 };
37390
37391 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
37392     /**
37393      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
37394      */
37395     /**
37396      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
37397      * rendering into an Roo.Editor, defaults to false)
37398      */
37399     /**
37400      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
37401      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
37402      */
37403     /**
37404      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
37405      */
37406     /**
37407      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
37408      * the dropdown list (defaults to undefined, with no header element)
37409      */
37410
37411      /**
37412      * @cfg {String/Roo.Template} tpl The template to use to render the output
37413      */
37414      
37415     // private
37416     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
37417     /**
37418      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
37419      */
37420     listWidth: undefined,
37421     /**
37422      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
37423      * mode = 'remote' or 'text' if mode = 'local')
37424      */
37425     displayField: undefined,
37426     /**
37427      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
37428      * mode = 'remote' or 'value' if mode = 'local'). 
37429      * Note: use of a valueField requires the user make a selection
37430      * in order for a value to be mapped.
37431      */
37432     valueField: undefined,
37433     
37434     
37435     /**
37436      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
37437      * field's data value (defaults to the underlying DOM element's name)
37438      */
37439     hiddenName: undefined,
37440     /**
37441      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
37442      */
37443     listClass: '',
37444     /**
37445      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
37446      */
37447     selectedClass: 'x-combo-selected',
37448     /**
37449      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
37450      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
37451      * which displays a downward arrow icon).
37452      */
37453     triggerClass : 'x-form-arrow-trigger',
37454     /**
37455      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
37456      */
37457     shadow:'sides',
37458     /**
37459      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
37460      * anchor positions (defaults to 'tl-bl')
37461      */
37462     listAlign: 'tl-bl?',
37463     /**
37464      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
37465      */
37466     maxHeight: 300,
37467     /**
37468      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
37469      * query specified by the allQuery config option (defaults to 'query')
37470      */
37471     triggerAction: 'query',
37472     /**
37473      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
37474      * (defaults to 4, does not apply if editable = false)
37475      */
37476     minChars : 4,
37477     /**
37478      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
37479      * delay (typeAheadDelay) if it matches a known value (defaults to false)
37480      */
37481     typeAhead: false,
37482     /**
37483      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
37484      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
37485      */
37486     queryDelay: 500,
37487     /**
37488      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
37489      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
37490      */
37491     pageSize: 0,
37492     /**
37493      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
37494      * when editable = true (defaults to false)
37495      */
37496     selectOnFocus:false,
37497     /**
37498      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
37499      */
37500     queryParam: 'query',
37501     /**
37502      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
37503      * when mode = 'remote' (defaults to 'Loading...')
37504      */
37505     loadingText: 'Loading...',
37506     /**
37507      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
37508      */
37509     resizable: false,
37510     /**
37511      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
37512      */
37513     handleHeight : 8,
37514     /**
37515      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
37516      * traditional select (defaults to true)
37517      */
37518     editable: true,
37519     /**
37520      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
37521      */
37522     allQuery: '',
37523     /**
37524      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
37525      */
37526     mode: 'remote',
37527     /**
37528      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
37529      * listWidth has a higher value)
37530      */
37531     minListWidth : 70,
37532     /**
37533      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
37534      * allow the user to set arbitrary text into the field (defaults to false)
37535      */
37536     forceSelection:false,
37537     /**
37538      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
37539      * if typeAhead = true (defaults to 250)
37540      */
37541     typeAheadDelay : 250,
37542     /**
37543      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
37544      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
37545      */
37546     valueNotFoundText : undefined,
37547     /**
37548      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
37549      */
37550     blockFocus : false,
37551     
37552     /**
37553      * @cfg {Boolean} disableClear Disable showing of clear button.
37554      */
37555     disableClear : false,
37556     /**
37557      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
37558      */
37559     alwaysQuery : false,
37560     
37561     //private
37562     addicon : false,
37563     editicon: false,
37564     
37565     // element that contains real text value.. (when hidden is used..)
37566      
37567     // private
37568     onRender : function(ct, position){
37569         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
37570         if(this.hiddenName){
37571             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
37572                     'before', true);
37573             this.hiddenField.value =
37574                 this.hiddenValue !== undefined ? this.hiddenValue :
37575                 this.value !== undefined ? this.value : '';
37576
37577             // prevent input submission
37578             this.el.dom.removeAttribute('name');
37579              
37580              
37581         }
37582         if(Roo.isGecko){
37583             this.el.dom.setAttribute('autocomplete', 'off');
37584         }
37585
37586         var cls = 'x-combo-list';
37587
37588         this.list = new Roo.Layer({
37589             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
37590         });
37591
37592         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
37593         this.list.setWidth(lw);
37594         this.list.swallowEvent('mousewheel');
37595         this.assetHeight = 0;
37596
37597         if(this.title){
37598             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
37599             this.assetHeight += this.header.getHeight();
37600         }
37601
37602         this.innerList = this.list.createChild({cls:cls+'-inner'});
37603         this.innerList.on('mouseover', this.onViewOver, this);
37604         this.innerList.on('mousemove', this.onViewMove, this);
37605         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
37606         
37607         if(this.allowBlank && !this.pageSize && !this.disableClear){
37608             this.footer = this.list.createChild({cls:cls+'-ft'});
37609             this.pageTb = new Roo.Toolbar(this.footer);
37610            
37611         }
37612         if(this.pageSize){
37613             this.footer = this.list.createChild({cls:cls+'-ft'});
37614             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
37615                     {pageSize: this.pageSize});
37616             
37617         }
37618         
37619         if (this.pageTb && this.allowBlank && !this.disableClear) {
37620             var _this = this;
37621             this.pageTb.add(new Roo.Toolbar.Fill(), {
37622                 cls: 'x-btn-icon x-btn-clear',
37623                 text: '&#160;',
37624                 handler: function()
37625                 {
37626                     _this.collapse();
37627                     _this.clearValue();
37628                     _this.onSelect(false, -1);
37629                 }
37630             });
37631         }
37632         if (this.footer) {
37633             this.assetHeight += this.footer.getHeight();
37634         }
37635         
37636
37637         if(!this.tpl){
37638             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
37639         }
37640
37641         this.view = new Roo.View(this.innerList, this.tpl, {
37642             singleSelect:true, store: this.store, selectedClass: this.selectedClass
37643         });
37644
37645         this.view.on('click', this.onViewClick, this);
37646
37647         this.store.on('beforeload', this.onBeforeLoad, this);
37648         this.store.on('load', this.onLoad, this);
37649         this.store.on('loadexception', this.onLoadException, this);
37650
37651         if(this.resizable){
37652             this.resizer = new Roo.Resizable(this.list,  {
37653                pinned:true, handles:'se'
37654             });
37655             this.resizer.on('resize', function(r, w, h){
37656                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
37657                 this.listWidth = w;
37658                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
37659                 this.restrictHeight();
37660             }, this);
37661             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
37662         }
37663         if(!this.editable){
37664             this.editable = true;
37665             this.setEditable(false);
37666         }  
37667         
37668         
37669         if (typeof(this.events.add.listeners) != 'undefined') {
37670             
37671             this.addicon = this.wrap.createChild(
37672                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
37673        
37674             this.addicon.on('click', function(e) {
37675                 this.fireEvent('add', this);
37676             }, this);
37677         }
37678         if (typeof(this.events.edit.listeners) != 'undefined') {
37679             
37680             this.editicon = this.wrap.createChild(
37681                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
37682             if (this.addicon) {
37683                 this.editicon.setStyle('margin-left', '40px');
37684             }
37685             this.editicon.on('click', function(e) {
37686                 
37687                 // we fire even  if inothing is selected..
37688                 this.fireEvent('edit', this, this.lastData );
37689                 
37690             }, this);
37691         }
37692         
37693         
37694         
37695     },
37696
37697     // private
37698     initEvents : function(){
37699         Roo.form.ComboBox.superclass.initEvents.call(this);
37700
37701         this.keyNav = new Roo.KeyNav(this.el, {
37702             "up" : function(e){
37703                 this.inKeyMode = true;
37704                 this.selectPrev();
37705             },
37706
37707             "down" : function(e){
37708                 if(!this.isExpanded()){
37709                     this.onTriggerClick();
37710                 }else{
37711                     this.inKeyMode = true;
37712                     this.selectNext();
37713                 }
37714             },
37715
37716             "enter" : function(e){
37717                 this.onViewClick();
37718                 //return true;
37719             },
37720
37721             "esc" : function(e){
37722                 this.collapse();
37723             },
37724
37725             "tab" : function(e){
37726                 this.onViewClick(false);
37727                 this.fireEvent("specialkey", this, e);
37728                 return true;
37729             },
37730
37731             scope : this,
37732
37733             doRelay : function(foo, bar, hname){
37734                 if(hname == 'down' || this.scope.isExpanded()){
37735                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
37736                 }
37737                 return true;
37738             },
37739
37740             forceKeyDown: true
37741         });
37742         this.queryDelay = Math.max(this.queryDelay || 10,
37743                 this.mode == 'local' ? 10 : 250);
37744         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
37745         if(this.typeAhead){
37746             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
37747         }
37748         if(this.editable !== false){
37749             this.el.on("keyup", this.onKeyUp, this);
37750         }
37751         if(this.forceSelection){
37752             this.on('blur', this.doForce, this);
37753         }
37754     },
37755
37756     onDestroy : function(){
37757         if(this.view){
37758             this.view.setStore(null);
37759             this.view.el.removeAllListeners();
37760             this.view.el.remove();
37761             this.view.purgeListeners();
37762         }
37763         if(this.list){
37764             this.list.destroy();
37765         }
37766         if(this.store){
37767             this.store.un('beforeload', this.onBeforeLoad, this);
37768             this.store.un('load', this.onLoad, this);
37769             this.store.un('loadexception', this.onLoadException, this);
37770         }
37771         Roo.form.ComboBox.superclass.onDestroy.call(this);
37772     },
37773
37774     // private
37775     fireKey : function(e){
37776         if(e.isNavKeyPress() && !this.list.isVisible()){
37777             this.fireEvent("specialkey", this, e);
37778         }
37779     },
37780
37781     // private
37782     onResize: function(w, h){
37783         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
37784         
37785         if(typeof w != 'number'){
37786             // we do not handle it!?!?
37787             return;
37788         }
37789         var tw = this.trigger.getWidth();
37790         tw += this.addicon ? this.addicon.getWidth() : 0;
37791         tw += this.editicon ? this.editicon.getWidth() : 0;
37792         var x = w - tw;
37793         this.el.setWidth( this.adjustWidth('input', x));
37794             
37795         this.trigger.setStyle('left', x+'px');
37796         
37797         if(this.list && this.listWidth === undefined){
37798             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
37799             this.list.setWidth(lw);
37800             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
37801         }
37802         
37803     
37804         
37805     },
37806
37807     /**
37808      * Allow or prevent the user from directly editing the field text.  If false is passed,
37809      * the user will only be able to select from the items defined in the dropdown list.  This method
37810      * is the runtime equivalent of setting the 'editable' config option at config time.
37811      * @param {Boolean} value True to allow the user to directly edit the field text
37812      */
37813     setEditable : function(value){
37814         if(value == this.editable){
37815             return;
37816         }
37817         this.editable = value;
37818         if(!value){
37819             this.el.dom.setAttribute('readOnly', true);
37820             this.el.on('mousedown', this.onTriggerClick,  this);
37821             this.el.addClass('x-combo-noedit');
37822         }else{
37823             this.el.dom.setAttribute('readOnly', false);
37824             this.el.un('mousedown', this.onTriggerClick,  this);
37825             this.el.removeClass('x-combo-noedit');
37826         }
37827     },
37828
37829     // private
37830     onBeforeLoad : function(){
37831         if(!this.hasFocus){
37832             return;
37833         }
37834         this.innerList.update(this.loadingText ?
37835                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
37836         this.restrictHeight();
37837         this.selectedIndex = -1;
37838     },
37839
37840     // private
37841     onLoad : function(){
37842         if(!this.hasFocus){
37843             return;
37844         }
37845         if(this.store.getCount() > 0){
37846             this.expand();
37847             this.restrictHeight();
37848             if(this.lastQuery == this.allQuery){
37849                 if(this.editable){
37850                     this.el.dom.select();
37851                 }
37852                 if(!this.selectByValue(this.value, true)){
37853                     this.select(0, true);
37854                 }
37855             }else{
37856                 this.selectNext();
37857                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
37858                     this.taTask.delay(this.typeAheadDelay);
37859                 }
37860             }
37861         }else{
37862             this.onEmptyResults();
37863         }
37864         //this.el.focus();
37865     },
37866     // private
37867     onLoadException : function()
37868     {
37869         this.collapse();
37870         Roo.log(this.store.reader.jsonData);
37871         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
37872             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
37873         }
37874         
37875         
37876     },
37877     // private
37878     onTypeAhead : function(){
37879         if(this.store.getCount() > 0){
37880             var r = this.store.getAt(0);
37881             var newValue = r.data[this.displayField];
37882             var len = newValue.length;
37883             var selStart = this.getRawValue().length;
37884             if(selStart != len){
37885                 this.setRawValue(newValue);
37886                 this.selectText(selStart, newValue.length);
37887             }
37888         }
37889     },
37890
37891     // private
37892     onSelect : function(record, index){
37893         if(this.fireEvent('beforeselect', this, record, index) !== false){
37894             this.setFromData(index > -1 ? record.data : false);
37895             this.collapse();
37896             this.fireEvent('select', this, record, index);
37897         }
37898     },
37899
37900     /**
37901      * Returns the currently selected field value or empty string if no value is set.
37902      * @return {String} value The selected value
37903      */
37904     getValue : function(){
37905         if(this.valueField){
37906             return typeof this.value != 'undefined' ? this.value : '';
37907         }else{
37908             return Roo.form.ComboBox.superclass.getValue.call(this);
37909         }
37910     },
37911
37912     /**
37913      * Clears any text/value currently set in the field
37914      */
37915     clearValue : function(){
37916         if(this.hiddenField){
37917             this.hiddenField.value = '';
37918         }
37919         this.value = '';
37920         this.setRawValue('');
37921         this.lastSelectionText = '';
37922         this.applyEmptyText();
37923     },
37924
37925     /**
37926      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
37927      * will be displayed in the field.  If the value does not match the data value of an existing item,
37928      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
37929      * Otherwise the field will be blank (although the value will still be set).
37930      * @param {String} value The value to match
37931      */
37932     setValue : function(v){
37933         var text = v;
37934         if(this.valueField){
37935             var r = this.findRecord(this.valueField, v);
37936             if(r){
37937                 text = r.data[this.displayField];
37938             }else if(this.valueNotFoundText !== undefined){
37939                 text = this.valueNotFoundText;
37940             }
37941         }
37942         this.lastSelectionText = text;
37943         if(this.hiddenField){
37944             this.hiddenField.value = v;
37945         }
37946         Roo.form.ComboBox.superclass.setValue.call(this, text);
37947         this.value = v;
37948     },
37949     /**
37950      * @property {Object} the last set data for the element
37951      */
37952     
37953     lastData : false,
37954     /**
37955      * Sets the value of the field based on a object which is related to the record format for the store.
37956      * @param {Object} value the value to set as. or false on reset?
37957      */
37958     setFromData : function(o){
37959         var dv = ''; // display value
37960         var vv = ''; // value value..
37961         this.lastData = o;
37962         if (this.displayField) {
37963             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
37964         } else {
37965             // this is an error condition!!!
37966             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
37967         }
37968         
37969         if(this.valueField){
37970             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
37971         }
37972         if(this.hiddenField){
37973             this.hiddenField.value = vv;
37974             
37975             this.lastSelectionText = dv;
37976             Roo.form.ComboBox.superclass.setValue.call(this, dv);
37977             this.value = vv;
37978             return;
37979         }
37980         // no hidden field.. - we store the value in 'value', but still display
37981         // display field!!!!
37982         this.lastSelectionText = dv;
37983         Roo.form.ComboBox.superclass.setValue.call(this, dv);
37984         this.value = vv;
37985         
37986         
37987     },
37988     // private
37989     reset : function(){
37990         // overridden so that last data is reset..
37991         this.setValue(this.originalValue);
37992         this.clearInvalid();
37993         this.lastData = false;
37994     },
37995     // private
37996     findRecord : function(prop, value){
37997         var record;
37998         if(this.store.getCount() > 0){
37999             this.store.each(function(r){
38000                 if(r.data[prop] == value){
38001                     record = r;
38002                     return false;
38003                 }
38004                 return true;
38005             });
38006         }
38007         return record;
38008     },
38009     
38010     getName: function()
38011     {
38012         // returns hidden if it's set..
38013         if (!this.rendered) {return ''};
38014         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
38015         
38016     },
38017     // private
38018     onViewMove : function(e, t){
38019         this.inKeyMode = false;
38020     },
38021
38022     // private
38023     onViewOver : function(e, t){
38024         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
38025             return;
38026         }
38027         var item = this.view.findItemFromChild(t);
38028         if(item){
38029             var index = this.view.indexOf(item);
38030             this.select(index, false);
38031         }
38032     },
38033
38034     // private
38035     onViewClick : function(doFocus)
38036     {
38037         var index = this.view.getSelectedIndexes()[0];
38038         var r = this.store.getAt(index);
38039         if(r){
38040             this.onSelect(r, index);
38041         }
38042         if(doFocus !== false && !this.blockFocus){
38043             this.el.focus();
38044         }
38045     },
38046
38047     // private
38048     restrictHeight : function(){
38049         this.innerList.dom.style.height = '';
38050         var inner = this.innerList.dom;
38051         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
38052         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
38053         this.list.beginUpdate();
38054         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
38055         this.list.alignTo(this.el, this.listAlign);
38056         this.list.endUpdate();
38057     },
38058
38059     // private
38060     onEmptyResults : function(){
38061         this.collapse();
38062     },
38063
38064     /**
38065      * Returns true if the dropdown list is expanded, else false.
38066      */
38067     isExpanded : function(){
38068         return this.list.isVisible();
38069     },
38070
38071     /**
38072      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
38073      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
38074      * @param {String} value The data value of the item to select
38075      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
38076      * selected item if it is not currently in view (defaults to true)
38077      * @return {Boolean} True if the value matched an item in the list, else false
38078      */
38079     selectByValue : function(v, scrollIntoView){
38080         if(v !== undefined && v !== null){
38081             var r = this.findRecord(this.valueField || this.displayField, v);
38082             if(r){
38083                 this.select(this.store.indexOf(r), scrollIntoView);
38084                 return true;
38085             }
38086         }
38087         return false;
38088     },
38089
38090     /**
38091      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
38092      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
38093      * @param {Number} index The zero-based index of the list item to select
38094      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
38095      * selected item if it is not currently in view (defaults to true)
38096      */
38097     select : function(index, scrollIntoView){
38098         this.selectedIndex = index;
38099         this.view.select(index);
38100         if(scrollIntoView !== false){
38101             var el = this.view.getNode(index);
38102             if(el){
38103                 this.innerList.scrollChildIntoView(el, false);
38104             }
38105         }
38106     },
38107
38108     // private
38109     selectNext : function(){
38110         var ct = this.store.getCount();
38111         if(ct > 0){
38112             if(this.selectedIndex == -1){
38113                 this.select(0);
38114             }else if(this.selectedIndex < ct-1){
38115                 this.select(this.selectedIndex+1);
38116             }
38117         }
38118     },
38119
38120     // private
38121     selectPrev : function(){
38122         var ct = this.store.getCount();
38123         if(ct > 0){
38124             if(this.selectedIndex == -1){
38125                 this.select(0);
38126             }else if(this.selectedIndex != 0){
38127                 this.select(this.selectedIndex-1);
38128             }
38129         }
38130     },
38131
38132     // private
38133     onKeyUp : function(e){
38134         if(this.editable !== false && !e.isSpecialKey()){
38135             this.lastKey = e.getKey();
38136             this.dqTask.delay(this.queryDelay);
38137         }
38138     },
38139
38140     // private
38141     validateBlur : function(){
38142         return !this.list || !this.list.isVisible();   
38143     },
38144
38145     // private
38146     initQuery : function(){
38147         this.doQuery(this.getRawValue());
38148     },
38149
38150     // private
38151     doForce : function(){
38152         if(this.el.dom.value.length > 0){
38153             this.el.dom.value =
38154                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
38155             this.applyEmptyText();
38156         }
38157     },
38158
38159     /**
38160      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
38161      * query allowing the query action to be canceled if needed.
38162      * @param {String} query The SQL query to execute
38163      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
38164      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
38165      * saved in the current store (defaults to false)
38166      */
38167     doQuery : function(q, forceAll){
38168         if(q === undefined || q === null){
38169             q = '';
38170         }
38171         var qe = {
38172             query: q,
38173             forceAll: forceAll,
38174             combo: this,
38175             cancel:false
38176         };
38177         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
38178             return false;
38179         }
38180         q = qe.query;
38181         forceAll = qe.forceAll;
38182         if(forceAll === true || (q.length >= this.minChars)){
38183             if(this.lastQuery != q || this.alwaysQuery){
38184                 this.lastQuery = q;
38185                 if(this.mode == 'local'){
38186                     this.selectedIndex = -1;
38187                     if(forceAll){
38188                         this.store.clearFilter();
38189                     }else{
38190                         this.store.filter(this.displayField, q);
38191                     }
38192                     this.onLoad();
38193                 }else{
38194                     this.store.baseParams[this.queryParam] = q;
38195                     this.store.load({
38196                         params: this.getParams(q)
38197                     });
38198                     this.expand();
38199                 }
38200             }else{
38201                 this.selectedIndex = -1;
38202                 this.onLoad();   
38203             }
38204         }
38205     },
38206
38207     // private
38208     getParams : function(q){
38209         var p = {};
38210         //p[this.queryParam] = q;
38211         if(this.pageSize){
38212             p.start = 0;
38213             p.limit = this.pageSize;
38214         }
38215         return p;
38216     },
38217
38218     /**
38219      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
38220      */
38221     collapse : function(){
38222         if(!this.isExpanded()){
38223             return;
38224         }
38225         this.list.hide();
38226         Roo.get(document).un('mousedown', this.collapseIf, this);
38227         Roo.get(document).un('mousewheel', this.collapseIf, this);
38228         if (!this.editable) {
38229             Roo.get(document).un('keydown', this.listKeyPress, this);
38230         }
38231         this.fireEvent('collapse', this);
38232     },
38233
38234     // private
38235     collapseIf : function(e){
38236         if(!e.within(this.wrap) && !e.within(this.list)){
38237             this.collapse();
38238         }
38239     },
38240
38241     /**
38242      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
38243      */
38244     expand : function(){
38245         if(this.isExpanded() || !this.hasFocus){
38246             return;
38247         }
38248         this.list.alignTo(this.el, this.listAlign);
38249         this.list.show();
38250         Roo.get(document).on('mousedown', this.collapseIf, this);
38251         Roo.get(document).on('mousewheel', this.collapseIf, this);
38252         if (!this.editable) {
38253             Roo.get(document).on('keydown', this.listKeyPress, this);
38254         }
38255         
38256         this.fireEvent('expand', this);
38257     },
38258
38259     // private
38260     // Implements the default empty TriggerField.onTriggerClick function
38261     onTriggerClick : function(){
38262         if(this.disabled){
38263             return;
38264         }
38265         if(this.isExpanded()){
38266             this.collapse();
38267             if (!this.blockFocus) {
38268                 this.el.focus();
38269             }
38270             
38271         }else {
38272             this.hasFocus = true;
38273             if(this.triggerAction == 'all') {
38274                 this.doQuery(this.allQuery, true);
38275             } else {
38276                 this.doQuery(this.getRawValue());
38277             }
38278             if (!this.blockFocus) {
38279                 this.el.focus();
38280             }
38281         }
38282     },
38283     listKeyPress : function(e)
38284     {
38285         //Roo.log('listkeypress');
38286         // scroll to first matching element based on key pres..
38287         if (e.isSpecialKey()) {
38288             return false;
38289         }
38290         var k = String.fromCharCode(e.getKey()).toUpperCase();
38291         //Roo.log(k);
38292         var match  = false;
38293         var csel = this.view.getSelectedNodes();
38294         var cselitem = false;
38295         if (csel.length) {
38296             var ix = this.view.indexOf(csel[0]);
38297             cselitem  = this.store.getAt(ix);
38298             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
38299                 cselitem = false;
38300             }
38301             
38302         }
38303         
38304         this.store.each(function(v) { 
38305             if (cselitem) {
38306                 // start at existing selection.
38307                 if (cselitem.id == v.id) {
38308                     cselitem = false;
38309                 }
38310                 return;
38311             }
38312                 
38313             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
38314                 match = this.store.indexOf(v);
38315                 return false;
38316             }
38317         }, this);
38318         
38319         if (match === false) {
38320             return true; // no more action?
38321         }
38322         // scroll to?
38323         this.view.select(match);
38324         var sn = Roo.get(this.view.getSelectedNodes()[0])
38325         sn.scrollIntoView(sn.dom.parentNode, false);
38326     }
38327
38328     /** 
38329     * @cfg {Boolean} grow 
38330     * @hide 
38331     */
38332     /** 
38333     * @cfg {Number} growMin 
38334     * @hide 
38335     */
38336     /** 
38337     * @cfg {Number} growMax 
38338     * @hide 
38339     */
38340     /**
38341      * @hide
38342      * @method autoSize
38343      */
38344 });/*
38345  * Based on:
38346  * Ext JS Library 1.1.1
38347  * Copyright(c) 2006-2007, Ext JS, LLC.
38348  *
38349  * Originally Released Under LGPL - original licence link has changed is not relivant.
38350  *
38351  * Fork - LGPL
38352  * <script type="text/javascript">
38353  */
38354 /**
38355  * @class Roo.form.Checkbox
38356  * @extends Roo.form.Field
38357  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
38358  * @constructor
38359  * Creates a new Checkbox
38360  * @param {Object} config Configuration options
38361  */
38362 Roo.form.Checkbox = function(config){
38363     Roo.form.Checkbox.superclass.constructor.call(this, config);
38364     this.addEvents({
38365         /**
38366          * @event check
38367          * Fires when the checkbox is checked or unchecked.
38368              * @param {Roo.form.Checkbox} this This checkbox
38369              * @param {Boolean} checked The new checked value
38370              */
38371         check : true
38372     });
38373 };
38374
38375 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
38376     /**
38377      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
38378      */
38379     focusClass : undefined,
38380     /**
38381      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
38382      */
38383     fieldClass: "x-form-field",
38384     /**
38385      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
38386      */
38387     checked: false,
38388     /**
38389      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38390      * {tag: "input", type: "checkbox", autocomplete: "off"})
38391      */
38392     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
38393     /**
38394      * @cfg {String} boxLabel The text that appears beside the checkbox
38395      */
38396     boxLabel : "",
38397     /**
38398      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
38399      */  
38400     inputValue : '1',
38401     /**
38402      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
38403      */
38404      valueOff: '0', // value when not checked..
38405
38406     actionMode : 'viewEl', 
38407     //
38408     // private
38409     itemCls : 'x-menu-check-item x-form-item',
38410     groupClass : 'x-menu-group-item',
38411     inputType : 'hidden',
38412     
38413     
38414     inSetChecked: false, // check that we are not calling self...
38415     
38416     inputElement: false, // real input element?
38417     basedOn: false, // ????
38418     
38419     isFormField: true, // not sure where this is needed!!!!
38420
38421     onResize : function(){
38422         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
38423         if(!this.boxLabel){
38424             this.el.alignTo(this.wrap, 'c-c');
38425         }
38426     },
38427
38428     initEvents : function(){
38429         Roo.form.Checkbox.superclass.initEvents.call(this);
38430         this.el.on("click", this.onClick,  this);
38431         this.el.on("change", this.onClick,  this);
38432     },
38433
38434
38435     getResizeEl : function(){
38436         return this.wrap;
38437     },
38438
38439     getPositionEl : function(){
38440         return this.wrap;
38441     },
38442
38443     // private
38444     onRender : function(ct, position){
38445         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
38446         /*
38447         if(this.inputValue !== undefined){
38448             this.el.dom.value = this.inputValue;
38449         }
38450         */
38451         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
38452         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
38453         var viewEl = this.wrap.createChild({ 
38454             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
38455         this.viewEl = viewEl;   
38456         this.wrap.on('click', this.onClick,  this); 
38457         
38458         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
38459         this.el.on('propertychange', this.setFromHidden,  this);  //ie
38460         
38461         
38462         
38463         if(this.boxLabel){
38464             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
38465         //    viewEl.on('click', this.onClick,  this); 
38466         }
38467         //if(this.checked){
38468             this.setChecked(this.checked);
38469         //}else{
38470             //this.checked = this.el.dom;
38471         //}
38472
38473     },
38474
38475     // private
38476     initValue : Roo.emptyFn,
38477
38478     /**
38479      * Returns the checked state of the checkbox.
38480      * @return {Boolean} True if checked, else false
38481      */
38482     getValue : function(){
38483         if(this.el){
38484             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
38485         }
38486         return this.valueOff;
38487         
38488     },
38489
38490         // private
38491     onClick : function(){ 
38492         this.setChecked(!this.checked);
38493
38494         //if(this.el.dom.checked != this.checked){
38495         //    this.setValue(this.el.dom.checked);
38496        // }
38497     },
38498
38499     /**
38500      * Sets the checked state of the checkbox.
38501      * On is always based on a string comparison between inputValue and the param.
38502      * @param {Boolean/String} value - the value to set 
38503      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
38504      */
38505     setValue : function(v,suppressEvent){
38506         
38507         
38508         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
38509         //if(this.el && this.el.dom){
38510         //    this.el.dom.checked = this.checked;
38511         //    this.el.dom.defaultChecked = this.checked;
38512         //}
38513         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
38514         //this.fireEvent("check", this, this.checked);
38515     },
38516     // private..
38517     setChecked : function(state,suppressEvent)
38518     {
38519         if (this.inSetChecked) {
38520             this.checked = state;
38521             return;
38522         }
38523         
38524     
38525         if(this.wrap){
38526             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
38527         }
38528         this.checked = state;
38529         if(suppressEvent !== true){
38530             this.fireEvent('check', this, state);
38531         }
38532         this.inSetChecked = true;
38533         this.el.dom.value = state ? this.inputValue : this.valueOff;
38534         this.inSetChecked = false;
38535         
38536     },
38537     // handle setting of hidden value by some other method!!?!?
38538     setFromHidden: function()
38539     {
38540         if(!this.el){
38541             return;
38542         }
38543         //console.log("SET FROM HIDDEN");
38544         //alert('setFrom hidden');
38545         this.setValue(this.el.dom.value);
38546     },
38547     
38548     onDestroy : function()
38549     {
38550         if(this.viewEl){
38551             Roo.get(this.viewEl).remove();
38552         }
38553          
38554         Roo.form.Checkbox.superclass.onDestroy.call(this);
38555     }
38556
38557 });/*
38558  * Based on:
38559  * Ext JS Library 1.1.1
38560  * Copyright(c) 2006-2007, Ext JS, LLC.
38561  *
38562  * Originally Released Under LGPL - original licence link has changed is not relivant.
38563  *
38564  * Fork - LGPL
38565  * <script type="text/javascript">
38566  */
38567  
38568 /**
38569  * @class Roo.form.Radio
38570  * @extends Roo.form.Checkbox
38571  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
38572  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
38573  * @constructor
38574  * Creates a new Radio
38575  * @param {Object} config Configuration options
38576  */
38577 Roo.form.Radio = function(){
38578     Roo.form.Radio.superclass.constructor.apply(this, arguments);
38579 };
38580 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
38581     inputType: 'radio',
38582
38583     /**
38584      * If this radio is part of a group, it will return the selected value
38585      * @return {String}
38586      */
38587     getGroupValue : function(){
38588         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
38589     }
38590 });//<script type="text/javascript">
38591
38592 /*
38593  * Ext JS Library 1.1.1
38594  * Copyright(c) 2006-2007, Ext JS, LLC.
38595  * licensing@extjs.com
38596  * 
38597  * http://www.extjs.com/license
38598  */
38599  
38600  /*
38601   * 
38602   * Known bugs:
38603   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
38604   * - IE ? - no idea how much works there.
38605   * 
38606   * 
38607   * 
38608   */
38609  
38610
38611 /**
38612  * @class Ext.form.HtmlEditor
38613  * @extends Ext.form.Field
38614  * Provides a lightweight HTML Editor component.
38615  * WARNING - THIS CURRENTlY ONLY WORKS ON FIREFOX - USE FCKeditor for a cross platform version
38616  * 
38617  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
38618  * supported by this editor.</b><br/><br/>
38619  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
38620  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
38621  */
38622 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
38623       /**
38624      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
38625      */
38626     toolbars : false,
38627     /**
38628      * @cfg {String} createLinkText The default text for the create link prompt
38629      */
38630     createLinkText : 'Please enter the URL for the link:',
38631     /**
38632      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
38633      */
38634     defaultLinkValue : 'http:/'+'/',
38635    
38636      /**
38637      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
38638      *                        Roo.resizable.
38639      */
38640     resizable : false,
38641      /**
38642      * @cfg {Number} height (in pixels)
38643      */   
38644     height: 300,
38645    /**
38646      * @cfg {Number} width (in pixels)
38647      */   
38648     width: 500,
38649     
38650     /**
38651      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
38652      * 
38653      */
38654     stylesheets: false,
38655     
38656     // id of frame..
38657     frameId: false,
38658     
38659     // private properties
38660     validationEvent : false,
38661     deferHeight: true,
38662     initialized : false,
38663     activated : false,
38664     sourceEditMode : false,
38665     onFocus : Roo.emptyFn,
38666     iframePad:3,
38667     hideMode:'offsets',
38668     
38669     defaultAutoCreate : { // modified by initCompnoent..
38670         tag: "textarea",
38671         style:"width:500px;height:300px;",
38672         autocomplete: "off"
38673     },
38674
38675     // private
38676     initComponent : function(){
38677         this.addEvents({
38678             /**
38679              * @event initialize
38680              * Fires when the editor is fully initialized (including the iframe)
38681              * @param {HtmlEditor} this
38682              */
38683             initialize: true,
38684             /**
38685              * @event activate
38686              * Fires when the editor is first receives the focus. Any insertion must wait
38687              * until after this event.
38688              * @param {HtmlEditor} this
38689              */
38690             activate: true,
38691              /**
38692              * @event beforesync
38693              * Fires before the textarea is updated with content from the editor iframe. Return false
38694              * to cancel the sync.
38695              * @param {HtmlEditor} this
38696              * @param {String} html
38697              */
38698             beforesync: true,
38699              /**
38700              * @event beforepush
38701              * Fires before the iframe editor is updated with content from the textarea. Return false
38702              * to cancel the push.
38703              * @param {HtmlEditor} this
38704              * @param {String} html
38705              */
38706             beforepush: true,
38707              /**
38708              * @event sync
38709              * Fires when the textarea is updated with content from the editor iframe.
38710              * @param {HtmlEditor} this
38711              * @param {String} html
38712              */
38713             sync: true,
38714              /**
38715              * @event push
38716              * Fires when the iframe editor is updated with content from the textarea.
38717              * @param {HtmlEditor} this
38718              * @param {String} html
38719              */
38720             push: true,
38721              /**
38722              * @event editmodechange
38723              * Fires when the editor switches edit modes
38724              * @param {HtmlEditor} this
38725              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
38726              */
38727             editmodechange: true,
38728             /**
38729              * @event editorevent
38730              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
38731              * @param {HtmlEditor} this
38732              */
38733             editorevent: true
38734         });
38735         this.defaultAutoCreate =  {
38736             tag: "textarea",
38737             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
38738             autocomplete: "off"
38739         };
38740     },
38741
38742     /**
38743      * Protected method that will not generally be called directly. It
38744      * is called when the editor creates its toolbar. Override this method if you need to
38745      * add custom toolbar buttons.
38746      * @param {HtmlEditor} editor
38747      */
38748     createToolbar : function(editor){
38749         if (!editor.toolbars || !editor.toolbars.length) {
38750             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
38751         }
38752         
38753         for (var i =0 ; i < editor.toolbars.length;i++) {
38754             editor.toolbars[i] = Roo.factory(editor.toolbars[i], Roo.form.HtmlEditor);
38755             editor.toolbars[i].init(editor);
38756         }
38757          
38758         
38759     },
38760
38761     /**
38762      * Protected method that will not generally be called directly. It
38763      * is called when the editor initializes the iframe with HTML contents. Override this method if you
38764      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
38765      */
38766     getDocMarkup : function(){
38767         // body styles..
38768         var st = '';
38769         if (this.stylesheets === false) {
38770             
38771             Roo.get(document.head).select('style').each(function(node) {
38772                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
38773             });
38774             
38775             Roo.get(document.head).select('link').each(function(node) { 
38776                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
38777             });
38778             
38779         } else if (!this.stylesheets.length) {
38780                 // simple..
38781                 st = '<style type="text/css">' +
38782                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
38783                    '</style>';
38784         } else {
38785             Roo.each(this.stylesheets, function(s) {
38786                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
38787             });
38788             
38789         }
38790         
38791         return '<html><head>' + st  +
38792             //<style type="text/css">' +
38793             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
38794             //'</style>' +
38795             ' </head><body></body></html>';
38796     },
38797
38798     // private
38799     onRender : function(ct, position)
38800     {
38801         var _t = this;
38802         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
38803         this.el.dom.style.border = '0 none';
38804         this.el.dom.setAttribute('tabIndex', -1);
38805         this.el.addClass('x-hidden');
38806         if(Roo.isIE){ // fix IE 1px bogus margin
38807             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
38808         }
38809         this.wrap = this.el.wrap({
38810             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
38811         });
38812         
38813         if (this.resizable) {
38814             this.resizeEl = new Roo.Resizable(this.wrap, {
38815                 pinned : true,
38816                 wrap: true,
38817                 dynamic : true,
38818                 minHeight : this.height,
38819                 height: this.height,
38820                 handles : this.resizable,
38821                 width: this.width,
38822                 listeners : {
38823                     resize : function(r, w, h) {
38824                         _t.onResize(w,h); // -something
38825                     }
38826                 }
38827             });
38828             
38829         }
38830
38831         this.frameId = Roo.id();
38832         
38833         this.createToolbar(this);
38834         
38835       
38836         
38837         var iframe = this.wrap.createChild({
38838             tag: 'iframe',
38839             id: this.frameId,
38840             name: this.frameId,
38841             frameBorder : 'no',
38842             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
38843         }, this.el
38844         );
38845         
38846        // console.log(iframe);
38847         //this.wrap.dom.appendChild(iframe);
38848
38849         this.iframe = iframe.dom;
38850
38851          this.assignDocWin();
38852         
38853         this.doc.designMode = 'on';
38854        
38855         this.doc.open();
38856         this.doc.write(this.getDocMarkup());
38857         this.doc.close();
38858
38859         
38860         var task = { // must defer to wait for browser to be ready
38861             run : function(){
38862                 //console.log("run task?" + this.doc.readyState);
38863                 this.assignDocWin();
38864                 if(this.doc.body || this.doc.readyState == 'complete'){
38865                     try {
38866                         this.doc.designMode="on";
38867                     } catch (e) {
38868                         return;
38869                     }
38870                     Roo.TaskMgr.stop(task);
38871                     this.initEditor.defer(10, this);
38872                 }
38873             },
38874             interval : 10,
38875             duration:10000,
38876             scope: this
38877         };
38878         Roo.TaskMgr.start(task);
38879
38880         if(!this.width){
38881             this.setSize(this.wrap.getSize());
38882         }
38883         if (this.resizeEl) {
38884             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
38885             // should trigger onReize..
38886         }
38887     },
38888
38889     // private
38890     onResize : function(w, h)
38891     {
38892         //Roo.log('resize: ' +w + ',' + h );
38893         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
38894         if(this.el && this.iframe){
38895             if(typeof w == 'number'){
38896                 var aw = w - this.wrap.getFrameWidth('lr');
38897                 this.el.setWidth(this.adjustWidth('textarea', aw));
38898                 this.iframe.style.width = aw + 'px';
38899             }
38900             if(typeof h == 'number'){
38901                 var tbh = 0;
38902                 for (var i =0; i < this.toolbars.length;i++) {
38903                     // fixme - ask toolbars for heights?
38904                     tbh += this.toolbars[i].tb.el.getHeight();
38905                     if (this.toolbars[i].footer) {
38906                         tbh += this.toolbars[i].footer.el.getHeight();
38907                     }
38908                 }
38909                 
38910                 
38911                 
38912                 
38913                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
38914                 ah -= 5; // knock a few pixes off for look..
38915                 this.el.setHeight(this.adjustWidth('textarea', ah));
38916                 this.iframe.style.height = ah + 'px';
38917                 if(this.doc){
38918                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
38919                 }
38920             }
38921         }
38922     },
38923
38924     /**
38925      * Toggles the editor between standard and source edit mode.
38926      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
38927      */
38928     toggleSourceEdit : function(sourceEditMode){
38929         
38930         this.sourceEditMode = sourceEditMode === true;
38931         
38932         if(this.sourceEditMode){
38933           
38934             this.syncValue();
38935             this.iframe.className = 'x-hidden';
38936             this.el.removeClass('x-hidden');
38937             this.el.dom.removeAttribute('tabIndex');
38938             this.el.focus();
38939         }else{
38940              
38941             this.pushValue();
38942             this.iframe.className = '';
38943             this.el.addClass('x-hidden');
38944             this.el.dom.setAttribute('tabIndex', -1);
38945             this.deferFocus();
38946         }
38947         this.setSize(this.wrap.getSize());
38948         this.fireEvent('editmodechange', this, this.sourceEditMode);
38949     },
38950
38951     // private used internally
38952     createLink : function(){
38953         var url = prompt(this.createLinkText, this.defaultLinkValue);
38954         if(url && url != 'http:/'+'/'){
38955             this.relayCmd('createlink', url);
38956         }
38957     },
38958
38959     // private (for BoxComponent)
38960     adjustSize : Roo.BoxComponent.prototype.adjustSize,
38961
38962     // private (for BoxComponent)
38963     getResizeEl : function(){
38964         return this.wrap;
38965     },
38966
38967     // private (for BoxComponent)
38968     getPositionEl : function(){
38969         return this.wrap;
38970     },
38971
38972     // private
38973     initEvents : function(){
38974         this.originalValue = this.getValue();
38975     },
38976
38977     /**
38978      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
38979      * @method
38980      */
38981     markInvalid : Roo.emptyFn,
38982     /**
38983      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
38984      * @method
38985      */
38986     clearInvalid : Roo.emptyFn,
38987
38988     setValue : function(v){
38989         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
38990         this.pushValue();
38991     },
38992
38993     /**
38994      * Protected method that will not generally be called directly. If you need/want
38995      * custom HTML cleanup, this is the method you should override.
38996      * @param {String} html The HTML to be cleaned
38997      * return {String} The cleaned HTML
38998      */
38999     cleanHtml : function(html){
39000         html = String(html);
39001         if(html.length > 5){
39002             if(Roo.isSafari){ // strip safari nonsense
39003                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
39004             }
39005         }
39006         if(html == '&nbsp;'){
39007             html = '';
39008         }
39009         return html;
39010     },
39011
39012     /**
39013      * Protected method that will not generally be called directly. Syncs the contents
39014      * of the editor iframe with the textarea.
39015      */
39016     syncValue : function(){
39017         if(this.initialized){
39018             var bd = (this.doc.body || this.doc.documentElement);
39019             this.cleanUpPaste();
39020             var html = bd.innerHTML;
39021             if(Roo.isSafari){
39022                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
39023                 var m = bs.match(/text-align:(.*?);/i);
39024                 if(m && m[1]){
39025                     html = '<div style="'+m[0]+'">' + html + '</div>';
39026                 }
39027             }
39028             html = this.cleanHtml(html);
39029             if(this.fireEvent('beforesync', this, html) !== false){
39030                 this.el.dom.value = html;
39031                 this.fireEvent('sync', this, html);
39032             }
39033         }
39034     },
39035
39036     /**
39037      * Protected method that will not generally be called directly. Pushes the value of the textarea
39038      * into the iframe editor.
39039      */
39040     pushValue : function(){
39041         if(this.initialized){
39042             var v = this.el.dom.value;
39043             if(v.length < 1){
39044                 v = '&#160;';
39045             }
39046             
39047             if(this.fireEvent('beforepush', this, v) !== false){
39048                 var d = (this.doc.body || this.doc.documentElement);
39049                 d.innerHTML = v;
39050                 this.cleanUpPaste();
39051                 this.el.dom.value = d.innerHTML;
39052                 this.fireEvent('push', this, v);
39053             }
39054         }
39055     },
39056
39057     // private
39058     deferFocus : function(){
39059         this.focus.defer(10, this);
39060     },
39061
39062     // doc'ed in Field
39063     focus : function(){
39064         if(this.win && !this.sourceEditMode){
39065             this.win.focus();
39066         }else{
39067             this.el.focus();
39068         }
39069     },
39070     
39071     assignDocWin: function()
39072     {
39073         var iframe = this.iframe;
39074         
39075          if(Roo.isIE){
39076             this.doc = iframe.contentWindow.document;
39077             this.win = iframe.contentWindow;
39078         } else {
39079             if (!Roo.get(this.frameId)) {
39080                 return;
39081             }
39082             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
39083             this.win = Roo.get(this.frameId).dom.contentWindow;
39084         }
39085     },
39086     
39087     // private
39088     initEditor : function(){
39089         //console.log("INIT EDITOR");
39090         this.assignDocWin();
39091         
39092         
39093         
39094         this.doc.designMode="on";
39095         this.doc.open();
39096         this.doc.write(this.getDocMarkup());
39097         this.doc.close();
39098         
39099         var dbody = (this.doc.body || this.doc.documentElement);
39100         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
39101         // this copies styles from the containing element into thsi one..
39102         // not sure why we need all of this..
39103         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
39104         ss['background-attachment'] = 'fixed'; // w3c
39105         dbody.bgProperties = 'fixed'; // ie
39106         Roo.DomHelper.applyStyles(dbody, ss);
39107         Roo.EventManager.on(this.doc, {
39108             //'mousedown': this.onEditorEvent,
39109             'mouseup': this.onEditorEvent,
39110             'dblclick': this.onEditorEvent,
39111             'click': this.onEditorEvent,
39112             'keyup': this.onEditorEvent,
39113             buffer:100,
39114             scope: this
39115         });
39116         if(Roo.isGecko){
39117             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
39118         }
39119         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
39120             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
39121         }
39122         this.initialized = true;
39123
39124         this.fireEvent('initialize', this);
39125         this.pushValue();
39126     },
39127
39128     // private
39129     onDestroy : function(){
39130         
39131         
39132         
39133         if(this.rendered){
39134             
39135             for (var i =0; i < this.toolbars.length;i++) {
39136                 // fixme - ask toolbars for heights?
39137                 this.toolbars[i].onDestroy();
39138             }
39139             
39140             this.wrap.dom.innerHTML = '';
39141             this.wrap.remove();
39142         }
39143     },
39144
39145     // private
39146     onFirstFocus : function(){
39147         
39148         this.assignDocWin();
39149         
39150         
39151         this.activated = true;
39152         for (var i =0; i < this.toolbars.length;i++) {
39153             this.toolbars[i].onFirstFocus();
39154         }
39155        
39156         if(Roo.isGecko){ // prevent silly gecko errors
39157             this.win.focus();
39158             var s = this.win.getSelection();
39159             if(!s.focusNode || s.focusNode.nodeType != 3){
39160                 var r = s.getRangeAt(0);
39161                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
39162                 r.collapse(true);
39163                 this.deferFocus();
39164             }
39165             try{
39166                 this.execCmd('useCSS', true);
39167                 this.execCmd('styleWithCSS', false);
39168             }catch(e){}
39169         }
39170         this.fireEvent('activate', this);
39171     },
39172
39173     // private
39174     adjustFont: function(btn){
39175         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
39176         //if(Roo.isSafari){ // safari
39177         //    adjust *= 2;
39178        // }
39179         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
39180         if(Roo.isSafari){ // safari
39181             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
39182             v =  (v < 10) ? 10 : v;
39183             v =  (v > 48) ? 48 : v;
39184             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
39185             
39186         }
39187         
39188         
39189         v = Math.max(1, v+adjust);
39190         
39191         this.execCmd('FontSize', v  );
39192     },
39193
39194     onEditorEvent : function(e){
39195         this.fireEvent('editorevent', this, e);
39196       //  this.updateToolbar();
39197         this.syncValue();
39198     },
39199
39200     insertTag : function(tg)
39201     {
39202         // could be a bit smarter... -> wrap the current selected tRoo..
39203         
39204         this.execCmd("formatblock",   tg);
39205         
39206     },
39207     
39208     insertText : function(txt)
39209     {
39210         
39211         
39212         range = this.createRange();
39213         range.deleteContents();
39214                //alert(Sender.getAttribute('label'));
39215                
39216         range.insertNode(this.doc.createTextNode(txt));
39217     } ,
39218     
39219     // private
39220     relayBtnCmd : function(btn){
39221         this.relayCmd(btn.cmd);
39222     },
39223
39224     /**
39225      * Executes a Midas editor command on the editor document and performs necessary focus and
39226      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
39227      * @param {String} cmd The Midas command
39228      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
39229      */
39230     relayCmd : function(cmd, value){
39231         this.win.focus();
39232         this.execCmd(cmd, value);
39233         this.fireEvent('editorevent', this);
39234         //this.updateToolbar();
39235         this.deferFocus();
39236     },
39237
39238     /**
39239      * Executes a Midas editor command directly on the editor document.
39240      * For visual commands, you should use {@link #relayCmd} instead.
39241      * <b>This should only be called after the editor is initialized.</b>
39242      * @param {String} cmd The Midas command
39243      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
39244      */
39245     execCmd : function(cmd, value){
39246         this.doc.execCommand(cmd, false, value === undefined ? null : value);
39247         this.syncValue();
39248     },
39249
39250    
39251     /**
39252      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
39253      * to insert tRoo.
39254      * @param {String} text
39255      */
39256     insertAtCursor : function(text){
39257         if(!this.activated){
39258             return;
39259         }
39260         if(Roo.isIE){
39261             this.win.focus();
39262             var r = this.doc.selection.createRange();
39263             if(r){
39264                 r.collapse(true);
39265                 r.pasteHTML(text);
39266                 this.syncValue();
39267                 this.deferFocus();
39268             }
39269         }else if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
39270             this.win.focus();
39271             this.execCmd('InsertHTML', text);
39272             this.deferFocus();
39273         }
39274     },
39275  // private
39276     mozKeyPress : function(e){
39277         if(e.ctrlKey){
39278             var c = e.getCharCode(), cmd;
39279           
39280             if(c > 0){
39281                 c = String.fromCharCode(c).toLowerCase();
39282                 switch(c){
39283                     case 'b':
39284                         cmd = 'bold';
39285                     break;
39286                     case 'i':
39287                         cmd = 'italic';
39288                     break;
39289                     case 'u':
39290                         cmd = 'underline';
39291                     case 'v':
39292                         this.cleanUpPaste.defer(100, this);
39293                         return;
39294                     break;
39295                 }
39296                 if(cmd){
39297                     this.win.focus();
39298                     this.execCmd(cmd);
39299                     this.deferFocus();
39300                     e.preventDefault();
39301                 }
39302                 
39303             }
39304         }
39305     },
39306
39307     // private
39308     fixKeys : function(){ // load time branching for fastest keydown performance
39309         if(Roo.isIE){
39310             return function(e){
39311                 var k = e.getKey(), r;
39312                 if(k == e.TAB){
39313                     e.stopEvent();
39314                     r = this.doc.selection.createRange();
39315                     if(r){
39316                         r.collapse(true);
39317                         r.pasteHTML('&#160;&#160;&#160;&#160;');
39318                         this.deferFocus();
39319                     }
39320                     return;
39321                 }
39322                 
39323                 if(k == e.ENTER){
39324                     r = this.doc.selection.createRange();
39325                     if(r){
39326                         var target = r.parentElement();
39327                         if(!target || target.tagName.toLowerCase() != 'li'){
39328                             e.stopEvent();
39329                             r.pasteHTML('<br />');
39330                             r.collapse(false);
39331                             r.select();
39332                         }
39333                     }
39334                 }
39335                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
39336                     this.cleanUpPaste.defer(100, this);
39337                     return;
39338                 }
39339                 
39340                 
39341             };
39342         }else if(Roo.isOpera){
39343             return function(e){
39344                 var k = e.getKey();
39345                 if(k == e.TAB){
39346                     e.stopEvent();
39347                     this.win.focus();
39348                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
39349                     this.deferFocus();
39350                 }
39351                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
39352                     this.cleanUpPaste.defer(100, this);
39353                     return;
39354                 }
39355                 
39356             };
39357         }else if(Roo.isSafari){
39358             return function(e){
39359                 var k = e.getKey();
39360                 
39361                 if(k == e.TAB){
39362                     e.stopEvent();
39363                     this.execCmd('InsertText','\t');
39364                     this.deferFocus();
39365                     return;
39366                 }
39367                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
39368                     this.cleanUpPaste.defer(100, this);
39369                     return;
39370                 }
39371                 
39372              };
39373         }
39374     }(),
39375     
39376     getAllAncestors: function()
39377     {
39378         var p = this.getSelectedNode();
39379         var a = [];
39380         if (!p) {
39381             a.push(p); // push blank onto stack..
39382             p = this.getParentElement();
39383         }
39384         
39385         
39386         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
39387             a.push(p);
39388             p = p.parentNode;
39389         }
39390         a.push(this.doc.body);
39391         return a;
39392     },
39393     lastSel : false,
39394     lastSelNode : false,
39395     
39396     
39397     getSelection : function() 
39398     {
39399         this.assignDocWin();
39400         return Roo.isIE ? this.doc.selection : this.win.getSelection();
39401     },
39402     
39403     getSelectedNode: function() 
39404     {
39405         // this may only work on Gecko!!!
39406         
39407         // should we cache this!!!!
39408         
39409         
39410         
39411          
39412         var range = this.createRange(this.getSelection()).cloneRange();
39413         
39414         if (Roo.isIE) {
39415             var parent = range.parentElement();
39416             while (true) {
39417                 var testRange = range.duplicate();
39418                 testRange.moveToElementText(parent);
39419                 if (testRange.inRange(range)) {
39420                     break;
39421                 }
39422                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
39423                     break;
39424                 }
39425                 parent = parent.parentElement;
39426             }
39427             return parent;
39428         }
39429         
39430         // is ancestor a text element.
39431         var ac =  range.commonAncestorContainer;
39432         if (ac.nodeType == 3) {
39433             ac = ac.parentNode;
39434         }
39435         
39436         var ar = ac.childNodes;
39437          
39438         var nodes = [];
39439         var other_nodes = [];
39440         var has_other_nodes = false;
39441         for (var i=0;i<ar.length;i++) {
39442             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
39443                 continue;
39444             }
39445             // fullly contained node.
39446             
39447             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
39448                 nodes.push(ar[i]);
39449                 continue;
39450             }
39451             
39452             // probably selected..
39453             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
39454                 other_nodes.push(ar[i]);
39455                 continue;
39456             }
39457             // outer..
39458             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
39459                 continue;
39460             }
39461             
39462             
39463             has_other_nodes = true;
39464         }
39465         if (!nodes.length && other_nodes.length) {
39466             nodes= other_nodes;
39467         }
39468         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
39469             return false;
39470         }
39471         
39472         return nodes[0];
39473     },
39474     createRange: function(sel)
39475     {
39476         // this has strange effects when using with 
39477         // top toolbar - not sure if it's a great idea.
39478         //this.editor.contentWindow.focus();
39479         if (typeof sel != "undefined") {
39480             try {
39481                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
39482             } catch(e) {
39483                 return this.doc.createRange();
39484             }
39485         } else {
39486             return this.doc.createRange();
39487         }
39488     },
39489     getParentElement: function()
39490     {
39491         
39492         this.assignDocWin();
39493         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
39494         
39495         var range = this.createRange(sel);
39496          
39497         try {
39498             var p = range.commonAncestorContainer;
39499             while (p.nodeType == 3) { // text node
39500                 p = p.parentNode;
39501             }
39502             return p;
39503         } catch (e) {
39504             return null;
39505         }
39506     
39507     },
39508     /***
39509      *
39510      * Range intersection.. the hard stuff...
39511      *  '-1' = before
39512      *  '0' = hits..
39513      *  '1' = after.
39514      *         [ -- selected range --- ]
39515      *   [fail]                        [fail]
39516      *
39517      *    basically..
39518      *      if end is before start or  hits it. fail.
39519      *      if start is after end or hits it fail.
39520      *
39521      *   if either hits (but other is outside. - then it's not 
39522      *   
39523      *    
39524      **/
39525     
39526     
39527     // @see http://www.thismuchiknow.co.uk/?p=64.
39528     rangeIntersectsNode : function(range, node)
39529     {
39530         var nodeRange = node.ownerDocument.createRange();
39531         try {
39532             nodeRange.selectNode(node);
39533         } catch (e) {
39534             nodeRange.selectNodeContents(node);
39535         }
39536     
39537         var rangeStartRange = range.cloneRange();
39538         rangeStartRange.collapse(true);
39539     
39540         var rangeEndRange = range.cloneRange();
39541         rangeEndRange.collapse(false);
39542     
39543         var nodeStartRange = nodeRange.cloneRange();
39544         nodeStartRange.collapse(true);
39545     
39546         var nodeEndRange = nodeRange.cloneRange();
39547         nodeEndRange.collapse(false);
39548     
39549         return rangeStartRange.compareBoundaryPoints(
39550                  Range.START_TO_START, nodeEndRange) == -1 &&
39551                rangeEndRange.compareBoundaryPoints(
39552                  Range.START_TO_START, nodeStartRange) == 1;
39553         
39554          
39555     },
39556     rangeCompareNode : function(range, node)
39557     {
39558         var nodeRange = node.ownerDocument.createRange();
39559         try {
39560             nodeRange.selectNode(node);
39561         } catch (e) {
39562             nodeRange.selectNodeContents(node);
39563         }
39564         
39565         
39566         range.collapse(true);
39567     
39568         nodeRange.collapse(true);
39569      
39570         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
39571         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
39572          
39573         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
39574         
39575         var nodeIsBefore   =  ss == 1;
39576         var nodeIsAfter    = ee == -1;
39577         
39578         if (nodeIsBefore && nodeIsAfter)
39579             return 0; // outer
39580         if (!nodeIsBefore && nodeIsAfter)
39581             return 1; //right trailed.
39582         
39583         if (nodeIsBefore && !nodeIsAfter)
39584             return 2;  // left trailed.
39585         // fully contined.
39586         return 3;
39587     },
39588
39589     // private? - in a new class?
39590     cleanUpPaste :  function()
39591     {
39592         // cleans up the whole document..
39593       //  console.log('cleanuppaste');
39594         this.cleanUpChildren(this.doc.body);
39595         this.doc.body.innerHTML = this.cleanWordChars(this.doc.body.innerHTML);
39596         
39597     },
39598     
39599     cleanWordChars : function(input) {
39600         var he = Roo.form.HtmlEditor;
39601     
39602         var output = input;
39603         Roo.each(he.swapCodes, function(sw) { 
39604         
39605             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
39606             output = output.replace(swapper, sw[1]);
39607         });
39608         return output;
39609     },
39610     
39611     
39612     cleanUpChildren : function (n)
39613     {
39614         if (!n.childNodes.length) {
39615             return;
39616         }
39617         for (var i = n.childNodes.length-1; i > -1 ; i--) {
39618            this.cleanUpChild(n.childNodes[i]);
39619         }
39620     },
39621     
39622     
39623         
39624     
39625     cleanUpChild : function (node)
39626     {
39627         //console.log(node);
39628         if (node.nodeName == "#text") {
39629             // clean up silly Windows -- stuff?
39630             return; 
39631         }
39632         if (node.nodeName == "#comment") {
39633             node.parentNode.removeChild(node);
39634             // clean up silly Windows -- stuff?
39635             return; 
39636         }
39637         
39638         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
39639             // remove node.
39640             node.parentNode.removeChild(node);
39641             return;
39642             
39643         }
39644         
39645         var remove_keep_children= Roo.form.HtmlEditor.remove.indexOf(node.tagName.toLowerCase()) > -1;
39646         
39647         // remove <a name=....> as rendering on yahoo mailer is bored with this.
39648         
39649         if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
39650             remove_keep_children = true;
39651         }
39652         
39653         if (remove_keep_children) {
39654             this.cleanUpChildren(node);
39655             // inserts everything just before this node...
39656             while (node.childNodes.length) {
39657                 var cn = node.childNodes[0];
39658                 node.removeChild(cn);
39659                 node.parentNode.insertBefore(cn, node);
39660             }
39661             node.parentNode.removeChild(node);
39662             return;
39663         }
39664         
39665         if (!node.attributes || !node.attributes.length) {
39666             this.cleanUpChildren(node);
39667             return;
39668         }
39669         
39670         function cleanAttr(n,v)
39671         {
39672             
39673             if (v.match(/^\./) || v.match(/^\//)) {
39674                 return;
39675             }
39676             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
39677                 return;
39678             }
39679             Roo.log("(REMOVE)"+ node.tagName +'.' + n + '=' + v);
39680             node.removeAttribute(n);
39681             
39682         }
39683         
39684         function cleanStyle(n,v)
39685         {
39686             if (v.match(/expression/)) { //XSS?? should we even bother..
39687                 node.removeAttribute(n);
39688                 return;
39689             }
39690             
39691             
39692             var parts = v.split(/;/);
39693             Roo.each(parts, function(p) {
39694                 p = p.replace(/\s+/g,'');
39695                 if (!p.length) {
39696                     return true;
39697                 }
39698                 var l = p.split(':').shift().replace(/\s+/g,'');
39699                 
39700                 // only allow 'c whitelisted system attributes'
39701                 if (Roo.form.HtmlEditor.cwhite.indexOf(l) < 0) {
39702                     Roo.log('(REMOVE)' + node.tagName +'.' + n + ':'+l + '=' + v);
39703                     node.removeAttribute(n);
39704                     return false;
39705                 }
39706                 return true;
39707             });
39708             
39709             
39710         }
39711         
39712         
39713         for (var i = node.attributes.length-1; i > -1 ; i--) {
39714             var a = node.attributes[i];
39715             //console.log(a);
39716             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
39717                 node.removeAttribute(a.name);
39718                 return;
39719             }
39720             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
39721                 cleanAttr(a.name,a.value); // fixme..
39722                 return;
39723             }
39724             if (a.name == 'style') {
39725                 cleanStyle(a.name,a.value);
39726             }
39727             /// clean up MS crap..
39728             // tecnically this should be a list of valid class'es..
39729             
39730             
39731             if (a.name == 'class') {
39732                 if (a.value.match(/^Mso/)) {
39733                     node.className = '';
39734                 }
39735                 
39736                 if (a.value.match(/body/)) {
39737                     node.className = '';
39738                 }
39739             }
39740             
39741             // style cleanup!?
39742             // class cleanup?
39743             
39744         }
39745         
39746         
39747         this.cleanUpChildren(node);
39748         
39749         
39750     }
39751     
39752     
39753     // hide stuff that is not compatible
39754     /**
39755      * @event blur
39756      * @hide
39757      */
39758     /**
39759      * @event change
39760      * @hide
39761      */
39762     /**
39763      * @event focus
39764      * @hide
39765      */
39766     /**
39767      * @event specialkey
39768      * @hide
39769      */
39770     /**
39771      * @cfg {String} fieldClass @hide
39772      */
39773     /**
39774      * @cfg {String} focusClass @hide
39775      */
39776     /**
39777      * @cfg {String} autoCreate @hide
39778      */
39779     /**
39780      * @cfg {String} inputType @hide
39781      */
39782     /**
39783      * @cfg {String} invalidClass @hide
39784      */
39785     /**
39786      * @cfg {String} invalidText @hide
39787      */
39788     /**
39789      * @cfg {String} msgFx @hide
39790      */
39791     /**
39792      * @cfg {String} validateOnBlur @hide
39793      */
39794 });
39795
39796 Roo.form.HtmlEditor.white = [
39797         'area', 'br', 'img', 'input', 'hr', 'wbr',
39798         
39799        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
39800        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
39801        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
39802        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
39803        'table',   'ul',         'xmp', 
39804        
39805        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
39806       'thead',   'tr', 
39807      
39808       'dir', 'menu', 'ol', 'ul', 'dl',
39809        
39810       'embed',  'object'
39811 ];
39812
39813
39814 Roo.form.HtmlEditor.black = [
39815     //    'embed',  'object', // enable - backend responsiblity to clean thiese
39816         'applet', // 
39817         'base',   'basefont', 'bgsound', 'blink',  'body', 
39818         'frame',  'frameset', 'head',    'html',   'ilayer', 
39819         'iframe', 'layer',  'link',     'meta',    'object',   
39820         'script', 'style' ,'title',  'xml' // clean later..
39821 ];
39822 Roo.form.HtmlEditor.clean = [
39823     'script', 'style', 'title', 'xml'
39824 ];
39825 Roo.form.HtmlEditor.remove = [
39826     'font'
39827 ];
39828 // attributes..
39829
39830 Roo.form.HtmlEditor.ablack = [
39831     'on'
39832 ];
39833     
39834 Roo.form.HtmlEditor.aclean = [ 
39835     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
39836 ];
39837
39838 // protocols..
39839 Roo.form.HtmlEditor.pwhite= [
39840         'http',  'https',  'mailto'
39841 ];
39842
39843 // white listed style attributes.
39844 Roo.form.HtmlEditor.cwhite= [
39845         'text-align',
39846         'font-size'
39847 ];
39848
39849
39850 Roo.form.HtmlEditor.swapCodes   =[ 
39851     [    8211, "--" ], 
39852     [    8212, "--" ], 
39853     [    8216,  "'" ],  
39854     [    8217, "'" ],  
39855     [    8220, '"' ],  
39856     [    8221, '"' ],  
39857     [    8226, "*" ],  
39858     [    8230, "..." ]
39859 ]; 
39860
39861     // <script type="text/javascript">
39862 /*
39863  * Based on
39864  * Ext JS Library 1.1.1
39865  * Copyright(c) 2006-2007, Ext JS, LLC.
39866  *  
39867  
39868  */
39869
39870 /**
39871  * @class Roo.form.HtmlEditorToolbar1
39872  * Basic Toolbar
39873  * 
39874  * Usage:
39875  *
39876  new Roo.form.HtmlEditor({
39877     ....
39878     toolbars : [
39879         new Roo.form.HtmlEditorToolbar1({
39880             disable : { fonts: 1 , format: 1, ..., ... , ...],
39881             btns : [ .... ]
39882         })
39883     }
39884      
39885  * 
39886  * @cfg {Object} disable List of elements to disable..
39887  * @cfg {Array} btns List of additional buttons.
39888  * 
39889  * 
39890  * NEEDS Extra CSS? 
39891  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
39892  */
39893  
39894 Roo.form.HtmlEditor.ToolbarStandard = function(config)
39895 {
39896     
39897     Roo.apply(this, config);
39898     
39899     // default disabled, based on 'good practice'..
39900     this.disable = this.disable || {};
39901     Roo.applyIf(this.disable, {
39902         fontSize : true,
39903         colors : true,
39904         specialElements : true
39905     });
39906     
39907     
39908     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
39909     // dont call parent... till later.
39910 }
39911
39912 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
39913     
39914     tb: false,
39915     
39916     rendered: false,
39917     
39918     editor : false,
39919     /**
39920      * @cfg {Object} disable  List of toolbar elements to disable
39921          
39922      */
39923     disable : false,
39924       /**
39925      * @cfg {Array} fontFamilies An array of available font families
39926      */
39927     fontFamilies : [
39928         'Arial',
39929         'Courier New',
39930         'Tahoma',
39931         'Times New Roman',
39932         'Verdana'
39933     ],
39934     
39935     specialChars : [
39936            "&#169;",
39937           "&#174;",     
39938           "&#8482;",    
39939           "&#163;" ,    
39940          // "&#8212;",    
39941           "&#8230;",    
39942           "&#247;" ,    
39943         //  "&#225;" ,     ?? a acute?
39944            "&#8364;"    , //Euro
39945        //   "&#8220;"    ,
39946         //  "&#8221;"    ,
39947         //  "&#8226;"    ,
39948           "&#176;"  //   , // degrees
39949
39950          // "&#233;"     , // e ecute
39951          // "&#250;"     , // u ecute?
39952     ],
39953     
39954     specialElements : [
39955         {
39956             text: "Insert Table",
39957             xtype: 'MenuItem',
39958             xns : Roo.Menu,
39959             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
39960                 
39961         },
39962         {    
39963             text: "Insert Image",
39964             xtype: 'MenuItem',
39965             xns : Roo.Menu,
39966             ihtml : '<img src="about:blank"/>'
39967             
39968         }
39969         
39970          
39971     ],
39972     
39973     
39974     inputElements : [ 
39975             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
39976             "input:submit", "input:button", "select", "textarea", "label" ],
39977     formats : [
39978         ["p"] ,  
39979         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
39980         ["pre"],[ "code"], 
39981         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"]
39982     ],
39983      /**
39984      * @cfg {String} defaultFont default font to use.
39985      */
39986     defaultFont: 'tahoma',
39987    
39988     fontSelect : false,
39989     
39990     
39991     formatCombo : false,
39992     
39993     init : function(editor)
39994     {
39995         this.editor = editor;
39996         
39997         
39998         var fid = editor.frameId;
39999         var etb = this;
40000         function btn(id, toggle, handler){
40001             var xid = fid + '-'+ id ;
40002             return {
40003                 id : xid,
40004                 cmd : id,
40005                 cls : 'x-btn-icon x-edit-'+id,
40006                 enableToggle:toggle !== false,
40007                 scope: editor, // was editor...
40008                 handler:handler||editor.relayBtnCmd,
40009                 clickEvent:'mousedown',
40010                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
40011                 tabIndex:-1
40012             };
40013         }
40014         
40015         
40016         
40017         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
40018         this.tb = tb;
40019          // stop form submits
40020         tb.el.on('click', function(e){
40021             e.preventDefault(); // what does this do?
40022         });
40023
40024         if(!this.disable.font && !Roo.isSafari){
40025             /* why no safari for fonts
40026             editor.fontSelect = tb.el.createChild({
40027                 tag:'select',
40028                 tabIndex: -1,
40029                 cls:'x-font-select',
40030                 html: editor.createFontOptions()
40031             });
40032             editor.fontSelect.on('change', function(){
40033                 var font = editor.fontSelect.dom.value;
40034                 editor.relayCmd('fontname', font);
40035                 editor.deferFocus();
40036             }, editor);
40037             tb.add(
40038                 editor.fontSelect.dom,
40039                 '-'
40040             );
40041             */
40042         };
40043         if(!this.disable.formats){
40044             this.formatCombo = new Roo.form.ComboBox({
40045                 store: new Roo.data.SimpleStore({
40046                     id : 'tag',
40047                     fields: ['tag'],
40048                     data : this.formats // from states.js
40049                 }),
40050                 blockFocus : true,
40051                 //autoCreate : {tag: "div",  size: "20"},
40052                 displayField:'tag',
40053                 typeAhead: false,
40054                 mode: 'local',
40055                 editable : false,
40056                 triggerAction: 'all',
40057                 emptyText:'Add tag',
40058                 selectOnFocus:true,
40059                 width:135,
40060                 listeners : {
40061                     'select': function(c, r, i) {
40062                         editor.insertTag(r.get('tag'));
40063                         editor.focus();
40064                     }
40065                 }
40066
40067             });
40068             tb.addField(this.formatCombo);
40069             
40070         }
40071         
40072         if(!this.disable.format){
40073             tb.add(
40074                 btn('bold'),
40075                 btn('italic'),
40076                 btn('underline')
40077             );
40078         };
40079         if(!this.disable.fontSize){
40080             tb.add(
40081                 '-',
40082                 
40083                 
40084                 btn('increasefontsize', false, editor.adjustFont),
40085                 btn('decreasefontsize', false, editor.adjustFont)
40086             );
40087         };
40088         
40089         
40090         if(!this.disable.colors){
40091             tb.add(
40092                 '-', {
40093                     id:editor.frameId +'-forecolor',
40094                     cls:'x-btn-icon x-edit-forecolor',
40095                     clickEvent:'mousedown',
40096                     tooltip: this.buttonTips['forecolor'] || undefined,
40097                     tabIndex:-1,
40098                     menu : new Roo.menu.ColorMenu({
40099                         allowReselect: true,
40100                         focus: Roo.emptyFn,
40101                         value:'000000',
40102                         plain:true,
40103                         selectHandler: function(cp, color){
40104                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
40105                             editor.deferFocus();
40106                         },
40107                         scope: editor,
40108                         clickEvent:'mousedown'
40109                     })
40110                 }, {
40111                     id:editor.frameId +'backcolor',
40112                     cls:'x-btn-icon x-edit-backcolor',
40113                     clickEvent:'mousedown',
40114                     tooltip: this.buttonTips['backcolor'] || undefined,
40115                     tabIndex:-1,
40116                     menu : new Roo.menu.ColorMenu({
40117                         focus: Roo.emptyFn,
40118                         value:'FFFFFF',
40119                         plain:true,
40120                         allowReselect: true,
40121                         selectHandler: function(cp, color){
40122                             if(Roo.isGecko){
40123                                 editor.execCmd('useCSS', false);
40124                                 editor.execCmd('hilitecolor', color);
40125                                 editor.execCmd('useCSS', true);
40126                                 editor.deferFocus();
40127                             }else{
40128                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
40129                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
40130                                 editor.deferFocus();
40131                             }
40132                         },
40133                         scope:editor,
40134                         clickEvent:'mousedown'
40135                     })
40136                 }
40137             );
40138         };
40139         // now add all the items...
40140         
40141
40142         if(!this.disable.alignments){
40143             tb.add(
40144                 '-',
40145                 btn('justifyleft'),
40146                 btn('justifycenter'),
40147                 btn('justifyright')
40148             );
40149         };
40150
40151         //if(!Roo.isSafari){
40152             if(!this.disable.links){
40153                 tb.add(
40154                     '-',
40155                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
40156                 );
40157             };
40158
40159             if(!this.disable.lists){
40160                 tb.add(
40161                     '-',
40162                     btn('insertorderedlist'),
40163                     btn('insertunorderedlist')
40164                 );
40165             }
40166             if(!this.disable.sourceEdit){
40167                 tb.add(
40168                     '-',
40169                     btn('sourceedit', true, function(btn){
40170                         this.toggleSourceEdit(btn.pressed);
40171                     })
40172                 );
40173             }
40174         //}
40175         
40176         var smenu = { };
40177         // special menu.. - needs to be tidied up..
40178         if (!this.disable.special) {
40179             smenu = {
40180                 text: "&#169;",
40181                 cls: 'x-edit-none',
40182                 
40183                 menu : {
40184                     items : []
40185                 }
40186             };
40187             for (var i =0; i < this.specialChars.length; i++) {
40188                 smenu.menu.items.push({
40189                     
40190                     html: this.specialChars[i],
40191                     handler: function(a,b) {
40192                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
40193                         
40194                     },
40195                     tabIndex:-1
40196                 });
40197             }
40198             
40199             
40200             tb.add(smenu);
40201             
40202             
40203         }
40204          
40205         if (!this.disable.specialElements) {
40206             var semenu = {
40207                 text: "Other;",
40208                 cls: 'x-edit-none',
40209                 menu : {
40210                     items : []
40211                 }
40212             };
40213             for (var i =0; i < this.specialElements.length; i++) {
40214                 semenu.menu.items.push(
40215                     Roo.apply({ 
40216                         handler: function(a,b) {
40217                             editor.insertAtCursor(this.ihtml);
40218                         }
40219                     }, this.specialElements[i])
40220                 );
40221                     
40222             }
40223             
40224             tb.add(semenu);
40225             
40226             
40227         }
40228          
40229         
40230         if (this.btns) {
40231             for(var i =0; i< this.btns.length;i++) {
40232                 var b = this.btns[i];
40233                 b.cls =  'x-edit-none';
40234                 b.scope = editor;
40235                 tb.add(b);
40236             }
40237         
40238         }
40239         
40240         
40241         
40242         // disable everything...
40243         
40244         this.tb.items.each(function(item){
40245            if(item.id != editor.frameId+ '-sourceedit'){
40246                 item.disable();
40247             }
40248         });
40249         this.rendered = true;
40250         
40251         // the all the btns;
40252         editor.on('editorevent', this.updateToolbar, this);
40253         // other toolbars need to implement this..
40254         //editor.on('editmodechange', this.updateToolbar, this);
40255     },
40256     
40257     
40258     
40259     /**
40260      * Protected method that will not generally be called directly. It triggers
40261      * a toolbar update by reading the markup state of the current selection in the editor.
40262      */
40263     updateToolbar: function(){
40264
40265         if(!this.editor.activated){
40266             this.editor.onFirstFocus();
40267             return;
40268         }
40269
40270         var btns = this.tb.items.map, 
40271             doc = this.editor.doc,
40272             frameId = this.editor.frameId;
40273
40274         if(!this.disable.font && !Roo.isSafari){
40275             /*
40276             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
40277             if(name != this.fontSelect.dom.value){
40278                 this.fontSelect.dom.value = name;
40279             }
40280             */
40281         }
40282         if(!this.disable.format){
40283             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
40284             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
40285             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
40286         }
40287         if(!this.disable.alignments){
40288             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
40289             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
40290             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
40291         }
40292         if(!Roo.isSafari && !this.disable.lists){
40293             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
40294             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
40295         }
40296         
40297         var ans = this.editor.getAllAncestors();
40298         if (this.formatCombo) {
40299             
40300             
40301             var store = this.formatCombo.store;
40302             this.formatCombo.setValue("");
40303             for (var i =0; i < ans.length;i++) {
40304                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
40305                     // select it..
40306                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
40307                     break;
40308                 }
40309             }
40310         }
40311         
40312         
40313         
40314         // hides menus... - so this cant be on a menu...
40315         Roo.menu.MenuMgr.hideAll();
40316
40317         //this.editorsyncValue();
40318     },
40319    
40320     
40321     createFontOptions : function(){
40322         var buf = [], fs = this.fontFamilies, ff, lc;
40323         for(var i = 0, len = fs.length; i< len; i++){
40324             ff = fs[i];
40325             lc = ff.toLowerCase();
40326             buf.push(
40327                 '<option value="',lc,'" style="font-family:',ff,';"',
40328                     (this.defaultFont == lc ? ' selected="true">' : '>'),
40329                     ff,
40330                 '</option>'
40331             );
40332         }
40333         return buf.join('');
40334     },
40335     
40336     toggleSourceEdit : function(sourceEditMode){
40337         if(sourceEditMode === undefined){
40338             sourceEditMode = !this.sourceEditMode;
40339         }
40340         this.sourceEditMode = sourceEditMode === true;
40341         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
40342         // just toggle the button?
40343         if(btn.pressed !== this.editor.sourceEditMode){
40344             btn.toggle(this.editor.sourceEditMode);
40345             return;
40346         }
40347         
40348         if(this.sourceEditMode){
40349             this.tb.items.each(function(item){
40350                 if(item.cmd != 'sourceedit'){
40351                     item.disable();
40352                 }
40353             });
40354           
40355         }else{
40356             if(this.initialized){
40357                 this.tb.items.each(function(item){
40358                     item.enable();
40359                 });
40360             }
40361             
40362         }
40363         // tell the editor that it's been pressed..
40364         this.editor.toggleSourceEdit(sourceEditMode);
40365        
40366     },
40367      /**
40368      * Object collection of toolbar tooltips for the buttons in the editor. The key
40369      * is the command id associated with that button and the value is a valid QuickTips object.
40370      * For example:
40371 <pre><code>
40372 {
40373     bold : {
40374         title: 'Bold (Ctrl+B)',
40375         text: 'Make the selected text bold.',
40376         cls: 'x-html-editor-tip'
40377     },
40378     italic : {
40379         title: 'Italic (Ctrl+I)',
40380         text: 'Make the selected text italic.',
40381         cls: 'x-html-editor-tip'
40382     },
40383     ...
40384 </code></pre>
40385     * @type Object
40386      */
40387     buttonTips : {
40388         bold : {
40389             title: 'Bold (Ctrl+B)',
40390             text: 'Make the selected text bold.',
40391             cls: 'x-html-editor-tip'
40392         },
40393         italic : {
40394             title: 'Italic (Ctrl+I)',
40395             text: 'Make the selected text italic.',
40396             cls: 'x-html-editor-tip'
40397         },
40398         underline : {
40399             title: 'Underline (Ctrl+U)',
40400             text: 'Underline the selected text.',
40401             cls: 'x-html-editor-tip'
40402         },
40403         increasefontsize : {
40404             title: 'Grow Text',
40405             text: 'Increase the font size.',
40406             cls: 'x-html-editor-tip'
40407         },
40408         decreasefontsize : {
40409             title: 'Shrink Text',
40410             text: 'Decrease the font size.',
40411             cls: 'x-html-editor-tip'
40412         },
40413         backcolor : {
40414             title: 'Text Highlight Color',
40415             text: 'Change the background color of the selected text.',
40416             cls: 'x-html-editor-tip'
40417         },
40418         forecolor : {
40419             title: 'Font Color',
40420             text: 'Change the color of the selected text.',
40421             cls: 'x-html-editor-tip'
40422         },
40423         justifyleft : {
40424             title: 'Align Text Left',
40425             text: 'Align text to the left.',
40426             cls: 'x-html-editor-tip'
40427         },
40428         justifycenter : {
40429             title: 'Center Text',
40430             text: 'Center text in the editor.',
40431             cls: 'x-html-editor-tip'
40432         },
40433         justifyright : {
40434             title: 'Align Text Right',
40435             text: 'Align text to the right.',
40436             cls: 'x-html-editor-tip'
40437         },
40438         insertunorderedlist : {
40439             title: 'Bullet List',
40440             text: 'Start a bulleted list.',
40441             cls: 'x-html-editor-tip'
40442         },
40443         insertorderedlist : {
40444             title: 'Numbered List',
40445             text: 'Start a numbered list.',
40446             cls: 'x-html-editor-tip'
40447         },
40448         createlink : {
40449             title: 'Hyperlink',
40450             text: 'Make the selected text a hyperlink.',
40451             cls: 'x-html-editor-tip'
40452         },
40453         sourceedit : {
40454             title: 'Source Edit',
40455             text: 'Switch to source editing mode.',
40456             cls: 'x-html-editor-tip'
40457         }
40458     },
40459     // private
40460     onDestroy : function(){
40461         if(this.rendered){
40462             
40463             this.tb.items.each(function(item){
40464                 if(item.menu){
40465                     item.menu.removeAll();
40466                     if(item.menu.el){
40467                         item.menu.el.destroy();
40468                     }
40469                 }
40470                 item.destroy();
40471             });
40472              
40473         }
40474     },
40475     onFirstFocus: function() {
40476         this.tb.items.each(function(item){
40477            item.enable();
40478         });
40479     }
40480 });
40481
40482
40483
40484
40485 // <script type="text/javascript">
40486 /*
40487  * Based on
40488  * Ext JS Library 1.1.1
40489  * Copyright(c) 2006-2007, Ext JS, LLC.
40490  *  
40491  
40492  */
40493
40494  
40495 /**
40496  * @class Roo.form.HtmlEditor.ToolbarContext
40497  * Context Toolbar
40498  * 
40499  * Usage:
40500  *
40501  new Roo.form.HtmlEditor({
40502     ....
40503     toolbars : [
40504         { xtype: 'ToolbarStandard', styles : {} }
40505         { xtype: 'ToolbarContext', disable : {} }
40506     ]
40507 })
40508
40509      
40510  * 
40511  * @config : {Object} disable List of elements to disable.. (not done yet.)
40512  * @config : {Object} styles  Map of styles available.
40513  * 
40514  */
40515
40516 Roo.form.HtmlEditor.ToolbarContext = function(config)
40517 {
40518     
40519     Roo.apply(this, config);
40520     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
40521     // dont call parent... till later.
40522     this.styles = this.styles || {};
40523 }
40524 Roo.form.HtmlEditor.ToolbarContext.types = {
40525     'IMG' : {
40526         width : {
40527             title: "Width",
40528             width: 40
40529         },
40530         height:  {
40531             title: "Height",
40532             width: 40
40533         },
40534         align: {
40535             title: "Align",
40536             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
40537             width : 80
40538             
40539         },
40540         border: {
40541             title: "Border",
40542             width: 40
40543         },
40544         alt: {
40545             title: "Alt",
40546             width: 120
40547         },
40548         src : {
40549             title: "Src",
40550             width: 220
40551         }
40552         
40553     },
40554     'A' : {
40555         name : {
40556             title: "Name",
40557             width: 50
40558         },
40559         href:  {
40560             title: "Href",
40561             width: 220
40562         } // border?
40563         
40564     },
40565     'TABLE' : {
40566         rows : {
40567             title: "Rows",
40568             width: 20
40569         },
40570         cols : {
40571             title: "Cols",
40572             width: 20
40573         },
40574         width : {
40575             title: "Width",
40576             width: 40
40577         },
40578         height : {
40579             title: "Height",
40580             width: 40
40581         },
40582         border : {
40583             title: "Border",
40584             width: 20
40585         }
40586     },
40587     'TD' : {
40588         width : {
40589             title: "Width",
40590             width: 40
40591         },
40592         height : {
40593             title: "Height",
40594             width: 40
40595         },   
40596         align: {
40597             title: "Align",
40598             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
40599             width: 80
40600         },
40601         valign: {
40602             title: "Valign",
40603             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
40604             width: 80
40605         },
40606         colspan: {
40607             title: "Colspan",
40608             width: 20
40609             
40610         }
40611     },
40612     'INPUT' : {
40613         name : {
40614             title: "name",
40615             width: 120
40616         },
40617         value : {
40618             title: "Value",
40619             width: 120
40620         },
40621         width : {
40622             title: "Width",
40623             width: 40
40624         }
40625     },
40626     'LABEL' : {
40627         'for' : {
40628             title: "For",
40629             width: 120
40630         }
40631     },
40632     'TEXTAREA' : {
40633           name : {
40634             title: "name",
40635             width: 120
40636         },
40637         rows : {
40638             title: "Rows",
40639             width: 20
40640         },
40641         cols : {
40642             title: "Cols",
40643             width: 20
40644         }
40645     },
40646     'SELECT' : {
40647         name : {
40648             title: "name",
40649             width: 120
40650         },
40651         selectoptions : {
40652             title: "Options",
40653             width: 200
40654         }
40655     },
40656     
40657     // should we really allow this??
40658     // should this just be 
40659     'BODY' : {
40660         title : {
40661             title: "title",
40662             width: 200,
40663             disabled : true
40664         }
40665     },
40666     '*' : {
40667         // empty..
40668     }
40669 };
40670
40671
40672
40673 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
40674     
40675     tb: false,
40676     
40677     rendered: false,
40678     
40679     editor : false,
40680     /**
40681      * @cfg {Object} disable  List of toolbar elements to disable
40682          
40683      */
40684     disable : false,
40685     /**
40686      * @cfg {Object} styles List of styles 
40687      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
40688      *
40689      * These must be defined in the page, so they get rendered correctly..
40690      * .headline { }
40691      * TD.underline { }
40692      * 
40693      */
40694     styles : false,
40695     
40696     
40697     
40698     toolbars : false,
40699     
40700     init : function(editor)
40701     {
40702         this.editor = editor;
40703         
40704         
40705         var fid = editor.frameId;
40706         var etb = this;
40707         function btn(id, toggle, handler){
40708             var xid = fid + '-'+ id ;
40709             return {
40710                 id : xid,
40711                 cmd : id,
40712                 cls : 'x-btn-icon x-edit-'+id,
40713                 enableToggle:toggle !== false,
40714                 scope: editor, // was editor...
40715                 handler:handler||editor.relayBtnCmd,
40716                 clickEvent:'mousedown',
40717                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
40718                 tabIndex:-1
40719             };
40720         }
40721         // create a new element.
40722         var wdiv = editor.wrap.createChild({
40723                 tag: 'div'
40724             }, editor.wrap.dom.firstChild.nextSibling, true);
40725         
40726         // can we do this more than once??
40727         
40728          // stop form submits
40729       
40730  
40731         // disable everything...
40732         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
40733         this.toolbars = {};
40734            
40735         for (var i in  ty) {
40736           
40737             this.toolbars[i] = this.buildToolbar(ty[i],i);
40738         }
40739         this.tb = this.toolbars.BODY;
40740         this.tb.el.show();
40741         this.buildFooter();
40742         this.footer.show();
40743          
40744         this.rendered = true;
40745         
40746         // the all the btns;
40747         editor.on('editorevent', this.updateToolbar, this);
40748         // other toolbars need to implement this..
40749         //editor.on('editmodechange', this.updateToolbar, this);
40750     },
40751     
40752     
40753     
40754     /**
40755      * Protected method that will not generally be called directly. It triggers
40756      * a toolbar update by reading the markup state of the current selection in the editor.
40757      */
40758     updateToolbar: function(ignore_a,ignore_b,sel){
40759
40760         
40761         if(!this.editor.activated){
40762              this.editor.onFirstFocus();
40763             return;
40764         }
40765         var updateFooter = sel ? false : true;
40766         
40767         
40768         var ans = this.editor.getAllAncestors();
40769         
40770         // pick
40771         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
40772         
40773         if (!sel) { 
40774             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
40775             sel = sel ? sel : this.editor.doc.body;
40776             sel = sel.tagName.length ? sel : this.editor.doc.body;
40777             
40778         }
40779         // pick a menu that exists..
40780         var tn = sel.tagName.toUpperCase();
40781         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
40782         
40783         tn = sel.tagName.toUpperCase();
40784         
40785         var lastSel = this.tb.selectedNode
40786         
40787         this.tb.selectedNode = sel;
40788         
40789         // if current menu does not match..
40790         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
40791                 
40792             this.tb.el.hide();
40793             ///console.log("show: " + tn);
40794             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
40795             this.tb.el.show();
40796             // update name
40797             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
40798             
40799             
40800             // update attributes
40801             if (this.tb.fields) {
40802                 this.tb.fields.each(function(e) {
40803                    e.setValue(sel.getAttribute(e.name));
40804                 });
40805             }
40806             
40807             // update styles
40808             var st = this.tb.fields.item(0);
40809             st.store.removeAll();
40810             var cn = sel.className.split(/\s+/);
40811             
40812             var avs = [];
40813             if (this.styles['*']) {
40814                 
40815                 Roo.each(this.styles['*'], function(v) {
40816                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
40817                 });
40818             }
40819             if (this.styles[tn]) { 
40820                 Roo.each(this.styles[tn], function(v) {
40821                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
40822                 });
40823             }
40824             
40825             st.store.loadData(avs);
40826             st.collapse();
40827             st.setValue(cn);
40828             
40829             // flag our selected Node.
40830             this.tb.selectedNode = sel;
40831            
40832            
40833             Roo.menu.MenuMgr.hideAll();
40834
40835         }
40836         
40837         if (!updateFooter) {
40838             return;
40839         }
40840         // update the footer
40841         //
40842         var html = '';
40843         
40844         this.footerEls = ans.reverse();
40845         Roo.each(this.footerEls, function(a,i) {
40846             if (!a) { return; }
40847             html += html.length ? ' &gt; '  :  '';
40848             
40849             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
40850             
40851         });
40852        
40853         // 
40854         var sz = this.footDisp.up('td').getSize();
40855         this.footDisp.dom.style.width = (sz.width -10) + 'px';
40856         this.footDisp.dom.style.marginLeft = '5px';
40857         
40858         this.footDisp.dom.style.overflow = 'hidden';
40859         
40860         this.footDisp.dom.innerHTML = html;
40861             
40862         //this.editorsyncValue();
40863     },
40864    
40865        
40866     // private
40867     onDestroy : function(){
40868         if(this.rendered){
40869             
40870             this.tb.items.each(function(item){
40871                 if(item.menu){
40872                     item.menu.removeAll();
40873                     if(item.menu.el){
40874                         item.menu.el.destroy();
40875                     }
40876                 }
40877                 item.destroy();
40878             });
40879              
40880         }
40881     },
40882     onFirstFocus: function() {
40883         // need to do this for all the toolbars..
40884         this.tb.items.each(function(item){
40885            item.enable();
40886         });
40887     },
40888     buildToolbar: function(tlist, nm)
40889     {
40890         var editor = this.editor;
40891          // create a new element.
40892         var wdiv = editor.wrap.createChild({
40893                 tag: 'div'
40894             }, editor.wrap.dom.firstChild.nextSibling, true);
40895         
40896        
40897         var tb = new Roo.Toolbar(wdiv);
40898         // add the name..
40899         
40900         tb.add(nm+ ":&nbsp;");
40901         
40902         // styles...
40903         if (this.styles) {
40904             
40905             // this needs a multi-select checkbox...
40906             tb.addField( new Roo.form.ComboBox({
40907                 store: new Roo.data.SimpleStore({
40908                     id : 'val',
40909                     fields: ['val', 'selected'],
40910                     data : [] 
40911                 }),
40912                 name : 'className',
40913                 displayField:'val',
40914                 typeAhead: false,
40915                 mode: 'local',
40916                 editable : false,
40917                 triggerAction: 'all',
40918                 emptyText:'Select Style',
40919                 selectOnFocus:true,
40920                 width: 130,
40921                 listeners : {
40922                     'select': function(c, r, i) {
40923                         // initial support only for on class per el..
40924                         tb.selectedNode.className =  r ? r.get('val') : '';
40925                     }
40926                 }
40927     
40928             }));
40929         }
40930             
40931         
40932         
40933         for (var i in tlist) {
40934             
40935             var item = tlist[i];
40936             tb.add(item.title + ":&nbsp;");
40937             
40938             
40939             
40940             
40941             if (item.opts) {
40942                 // opts == pulldown..
40943                 tb.addField( new Roo.form.ComboBox({
40944                     store: new Roo.data.SimpleStore({
40945                         id : 'val',
40946                         fields: ['val'],
40947                         data : item.opts  
40948                     }),
40949                     name : i,
40950                     displayField:'val',
40951                     typeAhead: false,
40952                     mode: 'local',
40953                     editable : false,
40954                     triggerAction: 'all',
40955                     emptyText:'Select',
40956                     selectOnFocus:true,
40957                     width: item.width ? item.width  : 130,
40958                     listeners : {
40959                         'select': function(c, r, i) {
40960                             tb.selectedNode.setAttribute(c.name, r.get('val'));
40961                         }
40962                     }
40963
40964                 }));
40965                 continue;
40966                     
40967                  
40968                 
40969                 tb.addField( new Roo.form.TextField({
40970                     name: i,
40971                     width: 100,
40972                     //allowBlank:false,
40973                     value: ''
40974                 }));
40975                 continue;
40976             }
40977             tb.addField( new Roo.form.TextField({
40978                 name: i,
40979                 width: item.width,
40980                 //allowBlank:true,
40981                 value: '',
40982                 listeners: {
40983                     'change' : function(f, nv, ov) {
40984                         tb.selectedNode.setAttribute(f.name, nv);
40985                     }
40986                 }
40987             }));
40988              
40989         }
40990         tb.el.on('click', function(e){
40991             e.preventDefault(); // what does this do?
40992         });
40993         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
40994         tb.el.hide();
40995         tb.name = nm;
40996         // dont need to disable them... as they will get hidden
40997         return tb;
40998          
40999         
41000     },
41001     buildFooter : function()
41002     {
41003         
41004         var fel = this.editor.wrap.createChild();
41005         this.footer = new Roo.Toolbar(fel);
41006         // toolbar has scrolly on left / right?
41007         var footDisp= new Roo.Toolbar.Fill();
41008         var _t = this;
41009         this.footer.add(
41010             {
41011                 text : '&lt;',
41012                 xtype: 'Button',
41013                 handler : function() {
41014                     _t.footDisp.scrollTo('left',0,true)
41015                 }
41016             }
41017         );
41018         this.footer.add( footDisp );
41019         this.footer.add( 
41020             {
41021                 text : '&gt;',
41022                 xtype: 'Button',
41023                 handler : function() {
41024                     // no animation..
41025                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
41026                 }
41027             }
41028         );
41029         var fel = Roo.get(footDisp.el);
41030         fel.addClass('x-editor-context');
41031         this.footDispWrap = fel; 
41032         this.footDispWrap.overflow  = 'hidden';
41033         
41034         this.footDisp = fel.createChild();
41035         this.footDispWrap.on('click', this.onContextClick, this)
41036         
41037         
41038     },
41039     onContextClick : function (ev,dom)
41040     {
41041         ev.preventDefault();
41042         var  cn = dom.className;
41043         Roo.log(cn);
41044         if (!cn.match(/x-ed-loc-/)) {
41045             return;
41046         }
41047         var n = cn.split('-').pop();
41048         var ans = this.footerEls;
41049         var sel = ans[n];
41050         
41051          // pick
41052         var range = this.editor.createRange();
41053         
41054         range.selectNodeContents(sel);
41055         //range.selectNode(sel);
41056         
41057         
41058         var selection = this.editor.getSelection();
41059         selection.removeAllRanges();
41060         selection.addRange(range);
41061         
41062         
41063         
41064         this.updateToolbar(null, null, sel);
41065         
41066         
41067     }
41068     
41069     
41070     
41071     
41072     
41073 });
41074
41075
41076
41077
41078
41079 /*
41080  * Based on:
41081  * Ext JS Library 1.1.1
41082  * Copyright(c) 2006-2007, Ext JS, LLC.
41083  *
41084  * Originally Released Under LGPL - original licence link has changed is not relivant.
41085  *
41086  * Fork - LGPL
41087  * <script type="text/javascript">
41088  */
41089  
41090 /**
41091  * @class Roo.form.BasicForm
41092  * @extends Roo.util.Observable
41093  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
41094  * @constructor
41095  * @param {String/HTMLElement/Roo.Element} el The form element or its id
41096  * @param {Object} config Configuration options
41097  */
41098 Roo.form.BasicForm = function(el, config){
41099     this.allItems = [];
41100     this.childForms = [];
41101     Roo.apply(this, config);
41102     /*
41103      * The Roo.form.Field items in this form.
41104      * @type MixedCollection
41105      */
41106      
41107      
41108     this.items = new Roo.util.MixedCollection(false, function(o){
41109         return o.id || (o.id = Roo.id());
41110     });
41111     this.addEvents({
41112         /**
41113          * @event beforeaction
41114          * Fires before any action is performed. Return false to cancel the action.
41115          * @param {Form} this
41116          * @param {Action} action The action to be performed
41117          */
41118         beforeaction: true,
41119         /**
41120          * @event actionfailed
41121          * Fires when an action fails.
41122          * @param {Form} this
41123          * @param {Action} action The action that failed
41124          */
41125         actionfailed : true,
41126         /**
41127          * @event actioncomplete
41128          * Fires when an action is completed.
41129          * @param {Form} this
41130          * @param {Action} action The action that completed
41131          */
41132         actioncomplete : true
41133     });
41134     if(el){
41135         this.initEl(el);
41136     }
41137     Roo.form.BasicForm.superclass.constructor.call(this);
41138 };
41139
41140 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
41141     /**
41142      * @cfg {String} method
41143      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
41144      */
41145     /**
41146      * @cfg {DataReader} reader
41147      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
41148      * This is optional as there is built-in support for processing JSON.
41149      */
41150     /**
41151      * @cfg {DataReader} errorReader
41152      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
41153      * This is completely optional as there is built-in support for processing JSON.
41154      */
41155     /**
41156      * @cfg {String} url
41157      * The URL to use for form actions if one isn't supplied in the action options.
41158      */
41159     /**
41160      * @cfg {Boolean} fileUpload
41161      * Set to true if this form is a file upload.
41162      */
41163      
41164     /**
41165      * @cfg {Object} baseParams
41166      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
41167      */
41168      /**
41169      
41170     /**
41171      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
41172      */
41173     timeout: 30,
41174
41175     // private
41176     activeAction : null,
41177
41178     /**
41179      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
41180      * or setValues() data instead of when the form was first created.
41181      */
41182     trackResetOnLoad : false,
41183     
41184     
41185     /**
41186      * childForms - used for multi-tab forms
41187      * @type {Array}
41188      */
41189     childForms : false,
41190     
41191     /**
41192      * allItems - full list of fields.
41193      * @type {Array}
41194      */
41195     allItems : false,
41196     
41197     /**
41198      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
41199      * element by passing it or its id or mask the form itself by passing in true.
41200      * @type Mixed
41201      */
41202     waitMsgTarget : false,
41203
41204     // private
41205     initEl : function(el){
41206         this.el = Roo.get(el);
41207         this.id = this.el.id || Roo.id();
41208         this.el.on('submit', this.onSubmit, this);
41209         this.el.addClass('x-form');
41210     },
41211
41212     // private
41213     onSubmit : function(e){
41214         e.stopEvent();
41215     },
41216
41217     /**
41218      * Returns true if client-side validation on the form is successful.
41219      * @return Boolean
41220      */
41221     isValid : function(){
41222         var valid = true;
41223         this.items.each(function(f){
41224            if(!f.validate()){
41225                valid = false;
41226            }
41227         });
41228         return valid;
41229     },
41230
41231     /**
41232      * Returns true if any fields in this form have changed since their original load.
41233      * @return Boolean
41234      */
41235     isDirty : function(){
41236         var dirty = false;
41237         this.items.each(function(f){
41238            if(f.isDirty()){
41239                dirty = true;
41240                return false;
41241            }
41242         });
41243         return dirty;
41244     },
41245
41246     /**
41247      * Performs a predefined action (submit or load) or custom actions you define on this form.
41248      * @param {String} actionName The name of the action type
41249      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
41250      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
41251      * accept other config options):
41252      * <pre>
41253 Property          Type             Description
41254 ----------------  ---------------  ----------------------------------------------------------------------------------
41255 url               String           The url for the action (defaults to the form's url)
41256 method            String           The form method to use (defaults to the form's method, or POST if not defined)
41257 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
41258 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
41259                                    validate the form on the client (defaults to false)
41260      * </pre>
41261      * @return {BasicForm} this
41262      */
41263     doAction : function(action, options){
41264         if(typeof action == 'string'){
41265             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
41266         }
41267         if(this.fireEvent('beforeaction', this, action) !== false){
41268             this.beforeAction(action);
41269             action.run.defer(100, action);
41270         }
41271         return this;
41272     },
41273
41274     /**
41275      * Shortcut to do a submit action.
41276      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
41277      * @return {BasicForm} this
41278      */
41279     submit : function(options){
41280         this.doAction('submit', options);
41281         return this;
41282     },
41283
41284     /**
41285      * Shortcut to do a load action.
41286      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
41287      * @return {BasicForm} this
41288      */
41289     load : function(options){
41290         this.doAction('load', options);
41291         return this;
41292     },
41293
41294     /**
41295      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
41296      * @param {Record} record The record to edit
41297      * @return {BasicForm} this
41298      */
41299     updateRecord : function(record){
41300         record.beginEdit();
41301         var fs = record.fields;
41302         fs.each(function(f){
41303             var field = this.findField(f.name);
41304             if(field){
41305                 record.set(f.name, field.getValue());
41306             }
41307         }, this);
41308         record.endEdit();
41309         return this;
41310     },
41311
41312     /**
41313      * Loads an Roo.data.Record into this form.
41314      * @param {Record} record The record to load
41315      * @return {BasicForm} this
41316      */
41317     loadRecord : function(record){
41318         this.setValues(record.data);
41319         return this;
41320     },
41321
41322     // private
41323     beforeAction : function(action){
41324         var o = action.options;
41325         
41326        
41327         if(this.waitMsgTarget === true){
41328             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
41329         }else if(this.waitMsgTarget){
41330             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
41331             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
41332         }else {
41333             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
41334         }
41335          
41336     },
41337
41338     // private
41339     afterAction : function(action, success){
41340         this.activeAction = null;
41341         var o = action.options;
41342         
41343         if(this.waitMsgTarget === true){
41344             this.el.unmask();
41345         }else if(this.waitMsgTarget){
41346             this.waitMsgTarget.unmask();
41347         }else{
41348             Roo.MessageBox.updateProgress(1);
41349             Roo.MessageBox.hide();
41350         }
41351          
41352         if(success){
41353             if(o.reset){
41354                 this.reset();
41355             }
41356             Roo.callback(o.success, o.scope, [this, action]);
41357             this.fireEvent('actioncomplete', this, action);
41358             
41359         }else{
41360             Roo.callback(o.failure, o.scope, [this, action]);
41361             // show an error message if no failed handler is set..
41362             if (!this.hasListener('actionfailed')) {
41363                 Roo.MessageBox.alert("Error",
41364                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
41365                         action.result.errorMsg :
41366                         "Saving Failed, please check your entries"
41367                 );
41368             }
41369             
41370             this.fireEvent('actionfailed', this, action);
41371         }
41372         
41373     },
41374
41375     /**
41376      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
41377      * @param {String} id The value to search for
41378      * @return Field
41379      */
41380     findField : function(id){
41381         var field = this.items.get(id);
41382         if(!field){
41383             this.items.each(function(f){
41384                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
41385                     field = f;
41386                     return false;
41387                 }
41388             });
41389         }
41390         return field || null;
41391     },
41392
41393     /**
41394      * Add a secondary form to this one, 
41395      * Used to provide tabbed forms. One form is primary, with hidden values 
41396      * which mirror the elements from the other forms.
41397      * 
41398      * @param {Roo.form.Form} form to add.
41399      * 
41400      */
41401     addForm : function(form)
41402     {
41403        
41404         if (this.childForms.indexOf(form) > -1) {
41405             // already added..
41406             return;
41407         }
41408         this.childForms.push(form);
41409         var n = '';
41410         Roo.each(form.allItems, function (fe) {
41411             
41412             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
41413             if (this.findField(n)) { // already added..
41414                 return;
41415             }
41416             var add = new Roo.form.Hidden({
41417                 name : n
41418             });
41419             add.render(this.el);
41420             
41421             this.add( add );
41422         }, this);
41423         
41424     },
41425     /**
41426      * Mark fields in this form invalid in bulk.
41427      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
41428      * @return {BasicForm} this
41429      */
41430     markInvalid : function(errors){
41431         if(errors instanceof Array){
41432             for(var i = 0, len = errors.length; i < len; i++){
41433                 var fieldError = errors[i];
41434                 var f = this.findField(fieldError.id);
41435                 if(f){
41436                     f.markInvalid(fieldError.msg);
41437                 }
41438             }
41439         }else{
41440             var field, id;
41441             for(id in errors){
41442                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
41443                     field.markInvalid(errors[id]);
41444                 }
41445             }
41446         }
41447         Roo.each(this.childForms || [], function (f) {
41448             f.markInvalid(errors);
41449         });
41450         
41451         return this;
41452     },
41453
41454     /**
41455      * Set values for fields in this form in bulk.
41456      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
41457      * @return {BasicForm} this
41458      */
41459     setValues : function(values){
41460         if(values instanceof Array){ // array of objects
41461             for(var i = 0, len = values.length; i < len; i++){
41462                 var v = values[i];
41463                 var f = this.findField(v.id);
41464                 if(f){
41465                     f.setValue(v.value);
41466                     if(this.trackResetOnLoad){
41467                         f.originalValue = f.getValue();
41468                     }
41469                 }
41470             }
41471         }else{ // object hash
41472             var field, id;
41473             for(id in values){
41474                 if(typeof values[id] != 'function' && (field = this.findField(id))){
41475                     
41476                     if (field.setFromData && 
41477                         field.valueField && 
41478                         field.displayField &&
41479                         // combos' with local stores can 
41480                         // be queried via setValue()
41481                         // to set their value..
41482                         (field.store && !field.store.isLocal)
41483                         ) {
41484                         // it's a combo
41485                         var sd = { };
41486                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
41487                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
41488                         field.setFromData(sd);
41489                         
41490                     } else {
41491                         field.setValue(values[id]);
41492                     }
41493                     
41494                     
41495                     if(this.trackResetOnLoad){
41496                         field.originalValue = field.getValue();
41497                     }
41498                 }
41499             }
41500         }
41501          
41502         Roo.each(this.childForms || [], function (f) {
41503             f.setValues(values);
41504         });
41505                 
41506         return this;
41507     },
41508
41509     /**
41510      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
41511      * they are returned as an array.
41512      * @param {Boolean} asString
41513      * @return {Object}
41514      */
41515     getValues : function(asString){
41516         if (this.childForms) {
41517             // copy values from the child forms
41518             Roo.each(this.childForms, function (f) {
41519                 this.setValues(f.getValues());
41520             }, this);
41521         }
41522         
41523         
41524         
41525         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
41526         if(asString === true){
41527             return fs;
41528         }
41529         return Roo.urlDecode(fs);
41530     },
41531     
41532     /**
41533      * Returns the fields in this form as an object with key/value pairs. 
41534      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
41535      * @return {Object}
41536      */
41537     getFieldValues : function(with_hidden)
41538     {
41539         if (this.childForms) {
41540             // copy values from the child forms
41541             // should this call getFieldValues - probably not as we do not currently copy
41542             // hidden fields when we generate..
41543             Roo.each(this.childForms, function (f) {
41544                 this.setValues(f.getValues());
41545             }, this);
41546         }
41547         
41548         var ret = {};
41549         this.items.each(function(f){
41550             if (!f.getName()) {
41551                 return;
41552             }
41553             var v = f.getValue();
41554             // not sure if this supported any more..
41555             if ((typeof(v) == 'object') && f.getRawValue) {
41556                 v = f.getRawValue() ; // dates..
41557             }
41558             // combo boxes where name != hiddenName...
41559             if (f.name != f.getName()) {
41560                 ret[f.name] = f.getRawValue();
41561             }
41562             ret[f.getName()] = v;
41563         });
41564         
41565         return ret;
41566     },
41567
41568     /**
41569      * Clears all invalid messages in this form.
41570      * @return {BasicForm} this
41571      */
41572     clearInvalid : function(){
41573         this.items.each(function(f){
41574            f.clearInvalid();
41575         });
41576         
41577         Roo.each(this.childForms || [], function (f) {
41578             f.clearInvalid();
41579         });
41580         
41581         
41582         return this;
41583     },
41584
41585     /**
41586      * Resets this form.
41587      * @return {BasicForm} this
41588      */
41589     reset : function(){
41590         this.items.each(function(f){
41591             f.reset();
41592         });
41593         
41594         Roo.each(this.childForms || [], function (f) {
41595             f.reset();
41596         });
41597        
41598         
41599         return this;
41600     },
41601
41602     /**
41603      * Add Roo.form components to this form.
41604      * @param {Field} field1
41605      * @param {Field} field2 (optional)
41606      * @param {Field} etc (optional)
41607      * @return {BasicForm} this
41608      */
41609     add : function(){
41610         this.items.addAll(Array.prototype.slice.call(arguments, 0));
41611         return this;
41612     },
41613
41614
41615     /**
41616      * Removes a field from the items collection (does NOT remove its markup).
41617      * @param {Field} field
41618      * @return {BasicForm} this
41619      */
41620     remove : function(field){
41621         this.items.remove(field);
41622         return this;
41623     },
41624
41625     /**
41626      * Looks at the fields in this form, checks them for an id attribute,
41627      * and calls applyTo on the existing dom element with that id.
41628      * @return {BasicForm} this
41629      */
41630     render : function(){
41631         this.items.each(function(f){
41632             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
41633                 f.applyTo(f.id);
41634             }
41635         });
41636         return this;
41637     },
41638
41639     /**
41640      * Calls {@link Ext#apply} for all fields in this form with the passed object.
41641      * @param {Object} values
41642      * @return {BasicForm} this
41643      */
41644     applyToFields : function(o){
41645         this.items.each(function(f){
41646            Roo.apply(f, o);
41647         });
41648         return this;
41649     },
41650
41651     /**
41652      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
41653      * @param {Object} values
41654      * @return {BasicForm} this
41655      */
41656     applyIfToFields : function(o){
41657         this.items.each(function(f){
41658            Roo.applyIf(f, o);
41659         });
41660         return this;
41661     }
41662 });
41663
41664 // back compat
41665 Roo.BasicForm = Roo.form.BasicForm;/*
41666  * Based on:
41667  * Ext JS Library 1.1.1
41668  * Copyright(c) 2006-2007, Ext JS, LLC.
41669  *
41670  * Originally Released Under LGPL - original licence link has changed is not relivant.
41671  *
41672  * Fork - LGPL
41673  * <script type="text/javascript">
41674  */
41675
41676 /**
41677  * @class Roo.form.Form
41678  * @extends Roo.form.BasicForm
41679  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
41680  * @constructor
41681  * @param {Object} config Configuration options
41682  */
41683 Roo.form.Form = function(config){
41684     var xitems =  [];
41685     if (config.items) {
41686         xitems = config.items;
41687         delete config.items;
41688     }
41689    
41690     
41691     Roo.form.Form.superclass.constructor.call(this, null, config);
41692     this.url = this.url || this.action;
41693     if(!this.root){
41694         this.root = new Roo.form.Layout(Roo.applyIf({
41695             id: Roo.id()
41696         }, config));
41697     }
41698     this.active = this.root;
41699     /**
41700      * Array of all the buttons that have been added to this form via {@link addButton}
41701      * @type Array
41702      */
41703     this.buttons = [];
41704     this.allItems = [];
41705     this.addEvents({
41706         /**
41707          * @event clientvalidation
41708          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
41709          * @param {Form} this
41710          * @param {Boolean} valid true if the form has passed client-side validation
41711          */
41712         clientvalidation: true,
41713         /**
41714          * @event rendered
41715          * Fires when the form is rendered
41716          * @param {Roo.form.Form} form
41717          */
41718         rendered : true
41719     });
41720     
41721     if (this.progressUrl) {
41722             // push a hidden field onto the list of fields..
41723             this.addxtype( {
41724                     xns: Roo.form, 
41725                     xtype : 'Hidden', 
41726                     name : 'UPLOAD_IDENTIFIER' 
41727             });
41728         }
41729         
41730     
41731     Roo.each(xitems, this.addxtype, this);
41732     
41733     
41734     
41735 };
41736
41737 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
41738     /**
41739      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
41740      */
41741     /**
41742      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
41743      */
41744     /**
41745      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
41746      */
41747     buttonAlign:'center',
41748
41749     /**
41750      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
41751      */
41752     minButtonWidth:75,
41753
41754     /**
41755      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
41756      * This property cascades to child containers if not set.
41757      */
41758     labelAlign:'left',
41759
41760     /**
41761      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
41762      * fires a looping event with that state. This is required to bind buttons to the valid
41763      * state using the config value formBind:true on the button.
41764      */
41765     monitorValid : false,
41766
41767     /**
41768      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
41769      */
41770     monitorPoll : 200,
41771     
41772     /**
41773      * @cfg {String} progressUrl - Url to return progress data 
41774      */
41775     
41776     progressUrl : false,
41777   
41778     /**
41779      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
41780      * fields are added and the column is closed. If no fields are passed the column remains open
41781      * until end() is called.
41782      * @param {Object} config The config to pass to the column
41783      * @param {Field} field1 (optional)
41784      * @param {Field} field2 (optional)
41785      * @param {Field} etc (optional)
41786      * @return Column The column container object
41787      */
41788     column : function(c){
41789         var col = new Roo.form.Column(c);
41790         this.start(col);
41791         if(arguments.length > 1){ // duplicate code required because of Opera
41792             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
41793             this.end();
41794         }
41795         return col;
41796     },
41797
41798     /**
41799      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
41800      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
41801      * until end() is called.
41802      * @param {Object} config The config to pass to the fieldset
41803      * @param {Field} field1 (optional)
41804      * @param {Field} field2 (optional)
41805      * @param {Field} etc (optional)
41806      * @return FieldSet The fieldset container object
41807      */
41808     fieldset : function(c){
41809         var fs = new Roo.form.FieldSet(c);
41810         this.start(fs);
41811         if(arguments.length > 1){ // duplicate code required because of Opera
41812             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
41813             this.end();
41814         }
41815         return fs;
41816     },
41817
41818     /**
41819      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
41820      * fields are added and the container is closed. If no fields are passed the container remains open
41821      * until end() is called.
41822      * @param {Object} config The config to pass to the Layout
41823      * @param {Field} field1 (optional)
41824      * @param {Field} field2 (optional)
41825      * @param {Field} etc (optional)
41826      * @return Layout The container object
41827      */
41828     container : function(c){
41829         var l = new Roo.form.Layout(c);
41830         this.start(l);
41831         if(arguments.length > 1){ // duplicate code required because of Opera
41832             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
41833             this.end();
41834         }
41835         return l;
41836     },
41837
41838     /**
41839      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
41840      * @param {Object} container A Roo.form.Layout or subclass of Layout
41841      * @return {Form} this
41842      */
41843     start : function(c){
41844         // cascade label info
41845         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
41846         this.active.stack.push(c);
41847         c.ownerCt = this.active;
41848         this.active = c;
41849         return this;
41850     },
41851
41852     /**
41853      * Closes the current open container
41854      * @return {Form} this
41855      */
41856     end : function(){
41857         if(this.active == this.root){
41858             return this;
41859         }
41860         this.active = this.active.ownerCt;
41861         return this;
41862     },
41863
41864     /**
41865      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
41866      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
41867      * as the label of the field.
41868      * @param {Field} field1
41869      * @param {Field} field2 (optional)
41870      * @param {Field} etc. (optional)
41871      * @return {Form} this
41872      */
41873     add : function(){
41874         this.active.stack.push.apply(this.active.stack, arguments);
41875         this.allItems.push.apply(this.allItems,arguments);
41876         var r = [];
41877         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
41878             if(a[i].isFormField){
41879                 r.push(a[i]);
41880             }
41881         }
41882         if(r.length > 0){
41883             Roo.form.Form.superclass.add.apply(this, r);
41884         }
41885         return this;
41886     },
41887     
41888
41889     
41890     
41891     
41892      /**
41893      * Find any element that has been added to a form, using it's ID or name
41894      * This can include framesets, columns etc. along with regular fields..
41895      * @param {String} id - id or name to find.
41896      
41897      * @return {Element} e - or false if nothing found.
41898      */
41899     findbyId : function(id)
41900     {
41901         var ret = false;
41902         if (!id) {
41903             return ret;
41904         }
41905         Roo.each(this.allItems, function(f){
41906             if (f.id == id || f.name == id ){
41907                 ret = f;
41908                 return false;
41909             }
41910         });
41911         return ret;
41912     },
41913
41914     
41915     
41916     /**
41917      * Render this form into the passed container. This should only be called once!
41918      * @param {String/HTMLElement/Element} container The element this component should be rendered into
41919      * @return {Form} this
41920      */
41921     render : function(ct)
41922     {
41923         
41924         
41925         
41926         ct = Roo.get(ct);
41927         var o = this.autoCreate || {
41928             tag: 'form',
41929             method : this.method || 'POST',
41930             id : this.id || Roo.id()
41931         };
41932         this.initEl(ct.createChild(o));
41933
41934         this.root.render(this.el);
41935         
41936        
41937              
41938         this.items.each(function(f){
41939             f.render('x-form-el-'+f.id);
41940         });
41941
41942         if(this.buttons.length > 0){
41943             // tables are required to maintain order and for correct IE layout
41944             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
41945                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
41946                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
41947             }}, null, true);
41948             var tr = tb.getElementsByTagName('tr')[0];
41949             for(var i = 0, len = this.buttons.length; i < len; i++) {
41950                 var b = this.buttons[i];
41951                 var td = document.createElement('td');
41952                 td.className = 'x-form-btn-td';
41953                 b.render(tr.appendChild(td));
41954             }
41955         }
41956         if(this.monitorValid){ // initialize after render
41957             this.startMonitoring();
41958         }
41959         this.fireEvent('rendered', this);
41960         return this;
41961     },
41962
41963     /**
41964      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
41965      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
41966      * object or a valid Roo.DomHelper element config
41967      * @param {Function} handler The function called when the button is clicked
41968      * @param {Object} scope (optional) The scope of the handler function
41969      * @return {Roo.Button}
41970      */
41971     addButton : function(config, handler, scope){
41972         var bc = {
41973             handler: handler,
41974             scope: scope,
41975             minWidth: this.minButtonWidth,
41976             hideParent:true
41977         };
41978         if(typeof config == "string"){
41979             bc.text = config;
41980         }else{
41981             Roo.apply(bc, config);
41982         }
41983         var btn = new Roo.Button(null, bc);
41984         this.buttons.push(btn);
41985         return btn;
41986     },
41987
41988      /**
41989      * Adds a series of form elements (using the xtype property as the factory method.
41990      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
41991      * @param {Object} config 
41992      */
41993     
41994     addxtype : function()
41995     {
41996         var ar = Array.prototype.slice.call(arguments, 0);
41997         var ret = false;
41998         for(var i = 0; i < ar.length; i++) {
41999             if (!ar[i]) {
42000                 continue; // skip -- if this happends something invalid got sent, we 
42001                 // should ignore it, as basically that interface element will not show up
42002                 // and that should be pretty obvious!!
42003             }
42004             
42005             if (Roo.form[ar[i].xtype]) {
42006                 ar[i].form = this;
42007                 var fe = Roo.factory(ar[i], Roo.form);
42008                 if (!ret) {
42009                     ret = fe;
42010                 }
42011                 fe.form = this;
42012                 if (fe.store) {
42013                     fe.store.form = this;
42014                 }
42015                 if (fe.isLayout) {  
42016                          
42017                     this.start(fe);
42018                     this.allItems.push(fe);
42019                     if (fe.items && fe.addxtype) {
42020                         fe.addxtype.apply(fe, fe.items);
42021                         delete fe.items;
42022                     }
42023                      this.end();
42024                     continue;
42025                 }
42026                 
42027                 
42028                  
42029                 this.add(fe);
42030               //  console.log('adding ' + ar[i].xtype);
42031             }
42032             if (ar[i].xtype == 'Button') {  
42033                 //console.log('adding button');
42034                 //console.log(ar[i]);
42035                 this.addButton(ar[i]);
42036                 this.allItems.push(fe);
42037                 continue;
42038             }
42039             
42040             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
42041                 alert('end is not supported on xtype any more, use items');
42042             //    this.end();
42043             //    //console.log('adding end');
42044             }
42045             
42046         }
42047         return ret;
42048     },
42049     
42050     /**
42051      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
42052      * option "monitorValid"
42053      */
42054     startMonitoring : function(){
42055         if(!this.bound){
42056             this.bound = true;
42057             Roo.TaskMgr.start({
42058                 run : this.bindHandler,
42059                 interval : this.monitorPoll || 200,
42060                 scope: this
42061             });
42062         }
42063     },
42064
42065     /**
42066      * Stops monitoring of the valid state of this form
42067      */
42068     stopMonitoring : function(){
42069         this.bound = false;
42070     },
42071
42072     // private
42073     bindHandler : function(){
42074         if(!this.bound){
42075             return false; // stops binding
42076         }
42077         var valid = true;
42078         this.items.each(function(f){
42079             if(!f.isValid(true)){
42080                 valid = false;
42081                 return false;
42082             }
42083         });
42084         for(var i = 0, len = this.buttons.length; i < len; i++){
42085             var btn = this.buttons[i];
42086             if(btn.formBind === true && btn.disabled === valid){
42087                 btn.setDisabled(!valid);
42088             }
42089         }
42090         this.fireEvent('clientvalidation', this, valid);
42091     }
42092     
42093     
42094     
42095     
42096     
42097     
42098     
42099     
42100 });
42101
42102
42103 // back compat
42104 Roo.Form = Roo.form.Form;
42105 /*
42106  * Based on:
42107  * Ext JS Library 1.1.1
42108  * Copyright(c) 2006-2007, Ext JS, LLC.
42109  *
42110  * Originally Released Under LGPL - original licence link has changed is not relivant.
42111  *
42112  * Fork - LGPL
42113  * <script type="text/javascript">
42114  */
42115  
42116  /**
42117  * @class Roo.form.Action
42118  * Internal Class used to handle form actions
42119  * @constructor
42120  * @param {Roo.form.BasicForm} el The form element or its id
42121  * @param {Object} config Configuration options
42122  */
42123  
42124  
42125 // define the action interface
42126 Roo.form.Action = function(form, options){
42127     this.form = form;
42128     this.options = options || {};
42129 };
42130 /**
42131  * Client Validation Failed
42132  * @const 
42133  */
42134 Roo.form.Action.CLIENT_INVALID = 'client';
42135 /**
42136  * Server Validation Failed
42137  * @const 
42138  */
42139  Roo.form.Action.SERVER_INVALID = 'server';
42140  /**
42141  * Connect to Server Failed
42142  * @const 
42143  */
42144 Roo.form.Action.CONNECT_FAILURE = 'connect';
42145 /**
42146  * Reading Data from Server Failed
42147  * @const 
42148  */
42149 Roo.form.Action.LOAD_FAILURE = 'load';
42150
42151 Roo.form.Action.prototype = {
42152     type : 'default',
42153     failureType : undefined,
42154     response : undefined,
42155     result : undefined,
42156
42157     // interface method
42158     run : function(options){
42159
42160     },
42161
42162     // interface method
42163     success : function(response){
42164
42165     },
42166
42167     // interface method
42168     handleResponse : function(response){
42169
42170     },
42171
42172     // default connection failure
42173     failure : function(response){
42174         
42175         this.response = response;
42176         this.failureType = Roo.form.Action.CONNECT_FAILURE;
42177         this.form.afterAction(this, false);
42178     },
42179
42180     processResponse : function(response){
42181         this.response = response;
42182         if(!response.responseText){
42183             return true;
42184         }
42185         this.result = this.handleResponse(response);
42186         return this.result;
42187     },
42188
42189     // utility functions used internally
42190     getUrl : function(appendParams){
42191         var url = this.options.url || this.form.url || this.form.el.dom.action;
42192         if(appendParams){
42193             var p = this.getParams();
42194             if(p){
42195                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
42196             }
42197         }
42198         return url;
42199     },
42200
42201     getMethod : function(){
42202         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
42203     },
42204
42205     getParams : function(){
42206         var bp = this.form.baseParams;
42207         var p = this.options.params;
42208         if(p){
42209             if(typeof p == "object"){
42210                 p = Roo.urlEncode(Roo.applyIf(p, bp));
42211             }else if(typeof p == 'string' && bp){
42212                 p += '&' + Roo.urlEncode(bp);
42213             }
42214         }else if(bp){
42215             p = Roo.urlEncode(bp);
42216         }
42217         return p;
42218     },
42219
42220     createCallback : function(){
42221         return {
42222             success: this.success,
42223             failure: this.failure,
42224             scope: this,
42225             timeout: (this.form.timeout*1000),
42226             upload: this.form.fileUpload ? this.success : undefined
42227         };
42228     }
42229 };
42230
42231 Roo.form.Action.Submit = function(form, options){
42232     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
42233 };
42234
42235 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
42236     type : 'submit',
42237
42238     haveProgress : false,
42239     uploadComplete : false,
42240     
42241     // uploadProgress indicator.
42242     uploadProgress : function()
42243     {
42244         if (!this.form.progressUrl) {
42245             return;
42246         }
42247         
42248         if (!this.haveProgress) {
42249             Roo.MessageBox.progress("Uploading", "Uploading");
42250         }
42251         if (this.uploadComplete) {
42252            Roo.MessageBox.hide();
42253            return;
42254         }
42255         
42256         this.haveProgress = true;
42257    
42258         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
42259         
42260         var c = new Roo.data.Connection();
42261         c.request({
42262             url : this.form.progressUrl,
42263             params: {
42264                 id : uid
42265             },
42266             method: 'GET',
42267             success : function(req){
42268                //console.log(data);
42269                 var rdata = false;
42270                 var edata;
42271                 try  {
42272                    rdata = Roo.decode(req.responseText)
42273                 } catch (e) {
42274                     Roo.log("Invalid data from server..");
42275                     Roo.log(edata);
42276                     return;
42277                 }
42278                 if (!rdata || !rdata.success) {
42279                     Roo.log(rdata);
42280                     return;
42281                 }
42282                 var data = rdata.data;
42283                 
42284                 if (this.uploadComplete) {
42285                    Roo.MessageBox.hide();
42286                    return;
42287                 }
42288                    
42289                 if (data){
42290                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
42291                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
42292                     );
42293                 }
42294                 this.uploadProgress.defer(2000,this);
42295             },
42296        
42297             failure: function(data) {
42298                 Roo.log('progress url failed ');
42299                 Roo.log(data);
42300             },
42301             scope : this
42302         });
42303            
42304     },
42305     
42306     
42307     run : function()
42308     {
42309         // run get Values on the form, so it syncs any secondary forms.
42310         this.form.getValues();
42311         
42312         var o = this.options;
42313         var method = this.getMethod();
42314         var isPost = method == 'POST';
42315         if(o.clientValidation === false || this.form.isValid()){
42316             
42317             if (this.form.progressUrl) {
42318                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
42319                     (new Date() * 1) + '' + Math.random());
42320                     
42321             } 
42322             
42323             
42324             Roo.Ajax.request(Roo.apply(this.createCallback(), {
42325                 form:this.form.el.dom,
42326                 url:this.getUrl(!isPost),
42327                 method: method,
42328                 params:isPost ? this.getParams() : null,
42329                 isUpload: this.form.fileUpload
42330             }));
42331             
42332             this.uploadProgress();
42333
42334         }else if (o.clientValidation !== false){ // client validation failed
42335             this.failureType = Roo.form.Action.CLIENT_INVALID;
42336             this.form.afterAction(this, false);
42337         }
42338     },
42339
42340     success : function(response)
42341     {
42342         this.uploadComplete= true;
42343         if (this.haveProgress) {
42344             Roo.MessageBox.hide();
42345         }
42346         
42347         
42348         var result = this.processResponse(response);
42349         if(result === true || result.success){
42350             this.form.afterAction(this, true);
42351             return;
42352         }
42353         if(result.errors){
42354             this.form.markInvalid(result.errors);
42355             this.failureType = Roo.form.Action.SERVER_INVALID;
42356         }
42357         this.form.afterAction(this, false);
42358     },
42359     failure : function(response)
42360     {
42361         this.uploadComplete= true;
42362         if (this.haveProgress) {
42363             Roo.MessageBox.hide();
42364         }
42365         
42366         this.response = response;
42367         this.failureType = Roo.form.Action.CONNECT_FAILURE;
42368         this.form.afterAction(this, false);
42369     },
42370     
42371     handleResponse : function(response){
42372         if(this.form.errorReader){
42373             var rs = this.form.errorReader.read(response);
42374             var errors = [];
42375             if(rs.records){
42376                 for(var i = 0, len = rs.records.length; i < len; i++) {
42377                     var r = rs.records[i];
42378                     errors[i] = r.data;
42379                 }
42380             }
42381             if(errors.length < 1){
42382                 errors = null;
42383             }
42384             return {
42385                 success : rs.success,
42386                 errors : errors
42387             };
42388         }
42389         var ret = false;
42390         try {
42391             ret = Roo.decode(response.responseText);
42392         } catch (e) {
42393             ret = {
42394                 success: false,
42395                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
42396                 errors : []
42397             };
42398         }
42399         return ret;
42400         
42401     }
42402 });
42403
42404
42405 Roo.form.Action.Load = function(form, options){
42406     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
42407     this.reader = this.form.reader;
42408 };
42409
42410 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
42411     type : 'load',
42412
42413     run : function(){
42414         
42415         Roo.Ajax.request(Roo.apply(
42416                 this.createCallback(), {
42417                     method:this.getMethod(),
42418                     url:this.getUrl(false),
42419                     params:this.getParams()
42420         }));
42421     },
42422
42423     success : function(response){
42424         
42425         var result = this.processResponse(response);
42426         if(result === true || !result.success || !result.data){
42427             this.failureType = Roo.form.Action.LOAD_FAILURE;
42428             this.form.afterAction(this, false);
42429             return;
42430         }
42431         this.form.clearInvalid();
42432         this.form.setValues(result.data);
42433         this.form.afterAction(this, true);
42434     },
42435
42436     handleResponse : function(response){
42437         if(this.form.reader){
42438             var rs = this.form.reader.read(response);
42439             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
42440             return {
42441                 success : rs.success,
42442                 data : data
42443             };
42444         }
42445         return Roo.decode(response.responseText);
42446     }
42447 });
42448
42449 Roo.form.Action.ACTION_TYPES = {
42450     'load' : Roo.form.Action.Load,
42451     'submit' : Roo.form.Action.Submit
42452 };/*
42453  * Based on:
42454  * Ext JS Library 1.1.1
42455  * Copyright(c) 2006-2007, Ext JS, LLC.
42456  *
42457  * Originally Released Under LGPL - original licence link has changed is not relivant.
42458  *
42459  * Fork - LGPL
42460  * <script type="text/javascript">
42461  */
42462  
42463 /**
42464  * @class Roo.form.Layout
42465  * @extends Roo.Component
42466  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
42467  * @constructor
42468  * @param {Object} config Configuration options
42469  */
42470 Roo.form.Layout = function(config){
42471     var xitems = [];
42472     if (config.items) {
42473         xitems = config.items;
42474         delete config.items;
42475     }
42476     Roo.form.Layout.superclass.constructor.call(this, config);
42477     this.stack = [];
42478     Roo.each(xitems, this.addxtype, this);
42479      
42480 };
42481
42482 Roo.extend(Roo.form.Layout, Roo.Component, {
42483     /**
42484      * @cfg {String/Object} autoCreate
42485      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
42486      */
42487     /**
42488      * @cfg {String/Object/Function} style
42489      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
42490      * a function which returns such a specification.
42491      */
42492     /**
42493      * @cfg {String} labelAlign
42494      * Valid values are "left," "top" and "right" (defaults to "left")
42495      */
42496     /**
42497      * @cfg {Number} labelWidth
42498      * Fixed width in pixels of all field labels (defaults to undefined)
42499      */
42500     /**
42501      * @cfg {Boolean} clear
42502      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
42503      */
42504     clear : true,
42505     /**
42506      * @cfg {String} labelSeparator
42507      * The separator to use after field labels (defaults to ':')
42508      */
42509     labelSeparator : ':',
42510     /**
42511      * @cfg {Boolean} hideLabels
42512      * True to suppress the display of field labels in this layout (defaults to false)
42513      */
42514     hideLabels : false,
42515
42516     // private
42517     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
42518     
42519     isLayout : true,
42520     
42521     // private
42522     onRender : function(ct, position){
42523         if(this.el){ // from markup
42524             this.el = Roo.get(this.el);
42525         }else {  // generate
42526             var cfg = this.getAutoCreate();
42527             this.el = ct.createChild(cfg, position);
42528         }
42529         if(this.style){
42530             this.el.applyStyles(this.style);
42531         }
42532         if(this.labelAlign){
42533             this.el.addClass('x-form-label-'+this.labelAlign);
42534         }
42535         if(this.hideLabels){
42536             this.labelStyle = "display:none";
42537             this.elementStyle = "padding-left:0;";
42538         }else{
42539             if(typeof this.labelWidth == 'number'){
42540                 this.labelStyle = "width:"+this.labelWidth+"px;";
42541                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
42542             }
42543             if(this.labelAlign == 'top'){
42544                 this.labelStyle = "width:auto;";
42545                 this.elementStyle = "padding-left:0;";
42546             }
42547         }
42548         var stack = this.stack;
42549         var slen = stack.length;
42550         if(slen > 0){
42551             if(!this.fieldTpl){
42552                 var t = new Roo.Template(
42553                     '<div class="x-form-item {5}">',
42554                         '<label for="{0}" style="{2}">{1}{4}</label>',
42555                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
42556                         '</div>',
42557                     '</div><div class="x-form-clear-left"></div>'
42558                 );
42559                 t.disableFormats = true;
42560                 t.compile();
42561                 Roo.form.Layout.prototype.fieldTpl = t;
42562             }
42563             for(var i = 0; i < slen; i++) {
42564                 if(stack[i].isFormField){
42565                     this.renderField(stack[i]);
42566                 }else{
42567                     this.renderComponent(stack[i]);
42568                 }
42569             }
42570         }
42571         if(this.clear){
42572             this.el.createChild({cls:'x-form-clear'});
42573         }
42574     },
42575
42576     // private
42577     renderField : function(f){
42578         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
42579                f.id, //0
42580                f.fieldLabel, //1
42581                f.labelStyle||this.labelStyle||'', //2
42582                this.elementStyle||'', //3
42583                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
42584                f.itemCls||this.itemCls||''  //5
42585        ], true).getPrevSibling());
42586     },
42587
42588     // private
42589     renderComponent : function(c){
42590         c.render(c.isLayout ? this.el : this.el.createChild());    
42591     },
42592     /**
42593      * Adds a object form elements (using the xtype property as the factory method.)
42594      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
42595      * @param {Object} config 
42596      */
42597     addxtype : function(o)
42598     {
42599         // create the lement.
42600         o.form = this.form;
42601         var fe = Roo.factory(o, Roo.form);
42602         this.form.allItems.push(fe);
42603         this.stack.push(fe);
42604         
42605         if (fe.isFormField) {
42606             this.form.items.add(fe);
42607         }
42608          
42609         return fe;
42610     }
42611 });
42612
42613 /**
42614  * @class Roo.form.Column
42615  * @extends Roo.form.Layout
42616  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
42617  * @constructor
42618  * @param {Object} config Configuration options
42619  */
42620 Roo.form.Column = function(config){
42621     Roo.form.Column.superclass.constructor.call(this, config);
42622 };
42623
42624 Roo.extend(Roo.form.Column, Roo.form.Layout, {
42625     /**
42626      * @cfg {Number/String} width
42627      * The fixed width of the column in pixels or CSS value (defaults to "auto")
42628      */
42629     /**
42630      * @cfg {String/Object} autoCreate
42631      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
42632      */
42633
42634     // private
42635     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
42636
42637     // private
42638     onRender : function(ct, position){
42639         Roo.form.Column.superclass.onRender.call(this, ct, position);
42640         if(this.width){
42641             this.el.setWidth(this.width);
42642         }
42643     }
42644 });
42645
42646
42647 /**
42648  * @class Roo.form.Row
42649  * @extends Roo.form.Layout
42650  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
42651  * @constructor
42652  * @param {Object} config Configuration options
42653  */
42654
42655  
42656 Roo.form.Row = function(config){
42657     Roo.form.Row.superclass.constructor.call(this, config);
42658 };
42659  
42660 Roo.extend(Roo.form.Row, Roo.form.Layout, {
42661       /**
42662      * @cfg {Number/String} width
42663      * The fixed width of the column in pixels or CSS value (defaults to "auto")
42664      */
42665     /**
42666      * @cfg {Number/String} height
42667      * The fixed height of the column in pixels or CSS value (defaults to "auto")
42668      */
42669     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
42670     
42671     padWidth : 20,
42672     // private
42673     onRender : function(ct, position){
42674         //console.log('row render');
42675         if(!this.rowTpl){
42676             var t = new Roo.Template(
42677                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
42678                     '<label for="{0}" style="{2}">{1}{4}</label>',
42679                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
42680                     '</div>',
42681                 '</div>'
42682             );
42683             t.disableFormats = true;
42684             t.compile();
42685             Roo.form.Layout.prototype.rowTpl = t;
42686         }
42687         this.fieldTpl = this.rowTpl;
42688         
42689         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
42690         var labelWidth = 100;
42691         
42692         if ((this.labelAlign != 'top')) {
42693             if (typeof this.labelWidth == 'number') {
42694                 labelWidth = this.labelWidth
42695             }
42696             this.padWidth =  20 + labelWidth;
42697             
42698         }
42699         
42700         Roo.form.Column.superclass.onRender.call(this, ct, position);
42701         if(this.width){
42702             this.el.setWidth(this.width);
42703         }
42704         if(this.height){
42705             this.el.setHeight(this.height);
42706         }
42707     },
42708     
42709     // private
42710     renderField : function(f){
42711         f.fieldEl = this.fieldTpl.append(this.el, [
42712                f.id, f.fieldLabel,
42713                f.labelStyle||this.labelStyle||'',
42714                this.elementStyle||'',
42715                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
42716                f.itemCls||this.itemCls||'',
42717                f.width ? f.width + this.padWidth : 160 + this.padWidth
42718        ],true);
42719     }
42720 });
42721  
42722
42723 /**
42724  * @class Roo.form.FieldSet
42725  * @extends Roo.form.Layout
42726  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
42727  * @constructor
42728  * @param {Object} config Configuration options
42729  */
42730 Roo.form.FieldSet = function(config){
42731     Roo.form.FieldSet.superclass.constructor.call(this, config);
42732 };
42733
42734 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
42735     /**
42736      * @cfg {String} legend
42737      * The text to display as the legend for the FieldSet (defaults to '')
42738      */
42739     /**
42740      * @cfg {String/Object} autoCreate
42741      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
42742      */
42743
42744     // private
42745     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
42746
42747     // private
42748     onRender : function(ct, position){
42749         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
42750         if(this.legend){
42751             this.setLegend(this.legend);
42752         }
42753     },
42754
42755     // private
42756     setLegend : function(text){
42757         if(this.rendered){
42758             this.el.child('legend').update(text);
42759         }
42760     }
42761 });/*
42762  * Based on:
42763  * Ext JS Library 1.1.1
42764  * Copyright(c) 2006-2007, Ext JS, LLC.
42765  *
42766  * Originally Released Under LGPL - original licence link has changed is not relivant.
42767  *
42768  * Fork - LGPL
42769  * <script type="text/javascript">
42770  */
42771 /**
42772  * @class Roo.form.VTypes
42773  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
42774  * @singleton
42775  */
42776 Roo.form.VTypes = function(){
42777     // closure these in so they are only created once.
42778     var alpha = /^[a-zA-Z_]+$/;
42779     var alphanum = /^[a-zA-Z0-9_]+$/;
42780     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
42781     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
42782
42783     // All these messages and functions are configurable
42784     return {
42785         /**
42786          * The function used to validate email addresses
42787          * @param {String} value The email address
42788          */
42789         'email' : function(v){
42790             return email.test(v);
42791         },
42792         /**
42793          * The error text to display when the email validation function returns false
42794          * @type String
42795          */
42796         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
42797         /**
42798          * The keystroke filter mask to be applied on email input
42799          * @type RegExp
42800          */
42801         'emailMask' : /[a-z0-9_\.\-@]/i,
42802
42803         /**
42804          * The function used to validate URLs
42805          * @param {String} value The URL
42806          */
42807         'url' : function(v){
42808             return url.test(v);
42809         },
42810         /**
42811          * The error text to display when the url validation function returns false
42812          * @type String
42813          */
42814         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
42815         
42816         /**
42817          * The function used to validate alpha values
42818          * @param {String} value The value
42819          */
42820         'alpha' : function(v){
42821             return alpha.test(v);
42822         },
42823         /**
42824          * The error text to display when the alpha validation function returns false
42825          * @type String
42826          */
42827         'alphaText' : 'This field should only contain letters and _',
42828         /**
42829          * The keystroke filter mask to be applied on alpha input
42830          * @type RegExp
42831          */
42832         'alphaMask' : /[a-z_]/i,
42833
42834         /**
42835          * The function used to validate alphanumeric values
42836          * @param {String} value The value
42837          */
42838         'alphanum' : function(v){
42839             return alphanum.test(v);
42840         },
42841         /**
42842          * The error text to display when the alphanumeric validation function returns false
42843          * @type String
42844          */
42845         'alphanumText' : 'This field should only contain letters, numbers and _',
42846         /**
42847          * The keystroke filter mask to be applied on alphanumeric input
42848          * @type RegExp
42849          */
42850         'alphanumMask' : /[a-z0-9_]/i
42851     };
42852 }();//<script type="text/javascript">
42853
42854 /**
42855  * @class Roo.form.FCKeditor
42856  * @extends Roo.form.TextArea
42857  * Wrapper around the FCKEditor http://www.fckeditor.net
42858  * @constructor
42859  * Creates a new FCKeditor
42860  * @param {Object} config Configuration options
42861  */
42862 Roo.form.FCKeditor = function(config){
42863     Roo.form.FCKeditor.superclass.constructor.call(this, config);
42864     this.addEvents({
42865          /**
42866          * @event editorinit
42867          * Fired when the editor is initialized - you can add extra handlers here..
42868          * @param {FCKeditor} this
42869          * @param {Object} the FCK object.
42870          */
42871         editorinit : true
42872     });
42873     
42874     
42875 };
42876 Roo.form.FCKeditor.editors = { };
42877 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
42878 {
42879     //defaultAutoCreate : {
42880     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
42881     //},
42882     // private
42883     /**
42884      * @cfg {Object} fck options - see fck manual for details.
42885      */
42886     fckconfig : false,
42887     
42888     /**
42889      * @cfg {Object} fck toolbar set (Basic or Default)
42890      */
42891     toolbarSet : 'Basic',
42892     /**
42893      * @cfg {Object} fck BasePath
42894      */ 
42895     basePath : '/fckeditor/',
42896     
42897     
42898     frame : false,
42899     
42900     value : '',
42901     
42902    
42903     onRender : function(ct, position)
42904     {
42905         if(!this.el){
42906             this.defaultAutoCreate = {
42907                 tag: "textarea",
42908                 style:"width:300px;height:60px;",
42909                 autocomplete: "off"
42910             };
42911         }
42912         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
42913         /*
42914         if(this.grow){
42915             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
42916             if(this.preventScrollbars){
42917                 this.el.setStyle("overflow", "hidden");
42918             }
42919             this.el.setHeight(this.growMin);
42920         }
42921         */
42922         //console.log('onrender' + this.getId() );
42923         Roo.form.FCKeditor.editors[this.getId()] = this;
42924          
42925
42926         this.replaceTextarea() ;
42927         
42928     },
42929     
42930     getEditor : function() {
42931         return this.fckEditor;
42932     },
42933     /**
42934      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
42935      * @param {Mixed} value The value to set
42936      */
42937     
42938     
42939     setValue : function(value)
42940     {
42941         //console.log('setValue: ' + value);
42942         
42943         if(typeof(value) == 'undefined') { // not sure why this is happending...
42944             return;
42945         }
42946         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
42947         
42948         //if(!this.el || !this.getEditor()) {
42949         //    this.value = value;
42950             //this.setValue.defer(100,this,[value]);    
42951         //    return;
42952         //} 
42953         
42954         if(!this.getEditor()) {
42955             return;
42956         }
42957         
42958         this.getEditor().SetData(value);
42959         
42960         //
42961
42962     },
42963
42964     /**
42965      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
42966      * @return {Mixed} value The field value
42967      */
42968     getValue : function()
42969     {
42970         
42971         if (this.frame && this.frame.dom.style.display == 'none') {
42972             return Roo.form.FCKeditor.superclass.getValue.call(this);
42973         }
42974         
42975         if(!this.el || !this.getEditor()) {
42976            
42977            // this.getValue.defer(100,this); 
42978             return this.value;
42979         }
42980        
42981         
42982         var value=this.getEditor().GetData();
42983         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
42984         return Roo.form.FCKeditor.superclass.getValue.call(this);
42985         
42986
42987     },
42988
42989     /**
42990      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
42991      * @return {Mixed} value The field value
42992      */
42993     getRawValue : function()
42994     {
42995         if (this.frame && this.frame.dom.style.display == 'none') {
42996             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
42997         }
42998         
42999         if(!this.el || !this.getEditor()) {
43000             //this.getRawValue.defer(100,this); 
43001             return this.value;
43002             return;
43003         }
43004         
43005         
43006         
43007         var value=this.getEditor().GetData();
43008         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
43009         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
43010          
43011     },
43012     
43013     setSize : function(w,h) {
43014         
43015         
43016         
43017         //if (this.frame && this.frame.dom.style.display == 'none') {
43018         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
43019         //    return;
43020         //}
43021         //if(!this.el || !this.getEditor()) {
43022         //    this.setSize.defer(100,this, [w,h]); 
43023         //    return;
43024         //}
43025         
43026         
43027         
43028         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
43029         
43030         this.frame.dom.setAttribute('width', w);
43031         this.frame.dom.setAttribute('height', h);
43032         this.frame.setSize(w,h);
43033         
43034     },
43035     
43036     toggleSourceEdit : function(value) {
43037         
43038       
43039          
43040         this.el.dom.style.display = value ? '' : 'none';
43041         this.frame.dom.style.display = value ?  'none' : '';
43042         
43043     },
43044     
43045     
43046     focus: function(tag)
43047     {
43048         if (this.frame.dom.style.display == 'none') {
43049             return Roo.form.FCKeditor.superclass.focus.call(this);
43050         }
43051         if(!this.el || !this.getEditor()) {
43052             this.focus.defer(100,this, [tag]); 
43053             return;
43054         }
43055         
43056         
43057         
43058         
43059         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
43060         this.getEditor().Focus();
43061         if (tgs.length) {
43062             if (!this.getEditor().Selection.GetSelection()) {
43063                 this.focus.defer(100,this, [tag]); 
43064                 return;
43065             }
43066             
43067             
43068             var r = this.getEditor().EditorDocument.createRange();
43069             r.setStart(tgs[0],0);
43070             r.setEnd(tgs[0],0);
43071             this.getEditor().Selection.GetSelection().removeAllRanges();
43072             this.getEditor().Selection.GetSelection().addRange(r);
43073             this.getEditor().Focus();
43074         }
43075         
43076     },
43077     
43078     
43079     
43080     replaceTextarea : function()
43081     {
43082         if ( document.getElementById( this.getId() + '___Frame' ) )
43083             return ;
43084         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
43085         //{
43086             // We must check the elements firstly using the Id and then the name.
43087         var oTextarea = document.getElementById( this.getId() );
43088         
43089         var colElementsByName = document.getElementsByName( this.getId() ) ;
43090          
43091         oTextarea.style.display = 'none' ;
43092
43093         if ( oTextarea.tabIndex ) {            
43094             this.TabIndex = oTextarea.tabIndex ;
43095         }
43096         
43097         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
43098         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
43099         this.frame = Roo.get(this.getId() + '___Frame')
43100     },
43101     
43102     _getConfigHtml : function()
43103     {
43104         var sConfig = '' ;
43105
43106         for ( var o in this.fckconfig ) {
43107             sConfig += sConfig.length > 0  ? '&amp;' : '';
43108             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
43109         }
43110
43111         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
43112     },
43113     
43114     
43115     _getIFrameHtml : function()
43116     {
43117         var sFile = 'fckeditor.html' ;
43118         /* no idea what this is about..
43119         try
43120         {
43121             if ( (/fcksource=true/i).test( window.top.location.search ) )
43122                 sFile = 'fckeditor.original.html' ;
43123         }
43124         catch (e) { 
43125         */
43126
43127         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
43128         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
43129         
43130         
43131         var html = '<iframe id="' + this.getId() +
43132             '___Frame" src="' + sLink +
43133             '" width="' + this.width +
43134             '" height="' + this.height + '"' +
43135             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
43136             ' frameborder="0" scrolling="no"></iframe>' ;
43137
43138         return html ;
43139     },
43140     
43141     _insertHtmlBefore : function( html, element )
43142     {
43143         if ( element.insertAdjacentHTML )       {
43144             // IE
43145             element.insertAdjacentHTML( 'beforeBegin', html ) ;
43146         } else { // Gecko
43147             var oRange = document.createRange() ;
43148             oRange.setStartBefore( element ) ;
43149             var oFragment = oRange.createContextualFragment( html );
43150             element.parentNode.insertBefore( oFragment, element ) ;
43151         }
43152     }
43153     
43154     
43155   
43156     
43157     
43158     
43159     
43160
43161 });
43162
43163 //Roo.reg('fckeditor', Roo.form.FCKeditor);
43164
43165 function FCKeditor_OnComplete(editorInstance){
43166     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
43167     f.fckEditor = editorInstance;
43168     //console.log("loaded");
43169     f.fireEvent('editorinit', f, editorInstance);
43170
43171   
43172
43173  
43174
43175
43176
43177
43178
43179
43180
43181
43182
43183
43184
43185
43186
43187
43188
43189 //<script type="text/javascript">
43190 /**
43191  * @class Roo.form.GridField
43192  * @extends Roo.form.Field
43193  * Embed a grid (or editable grid into a form)
43194  * STATUS ALPHA
43195  * 
43196  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
43197  * it needs 
43198  * xgrid.store = Roo.data.Store
43199  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
43200  * xgrid.store.reader = Roo.data.JsonReader 
43201  * 
43202  * 
43203  * @constructor
43204  * Creates a new GridField
43205  * @param {Object} config Configuration options
43206  */
43207 Roo.form.GridField = function(config){
43208     Roo.form.GridField.superclass.constructor.call(this, config);
43209      
43210 };
43211
43212 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
43213     /**
43214      * @cfg {Number} width  - used to restrict width of grid..
43215      */
43216     width : 100,
43217     /**
43218      * @cfg {Number} height - used to restrict height of grid..
43219      */
43220     height : 50,
43221      /**
43222      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
43223          * 
43224          *}
43225      */
43226     xgrid : false, 
43227     /**
43228      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
43229      * {tag: "input", type: "checkbox", autocomplete: "off"})
43230      */
43231    // defaultAutoCreate : { tag: 'div' },
43232     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
43233     /**
43234      * @cfg {String} addTitle Text to include for adding a title.
43235      */
43236     addTitle : false,
43237     //
43238     onResize : function(){
43239         Roo.form.Field.superclass.onResize.apply(this, arguments);
43240     },
43241
43242     initEvents : function(){
43243         // Roo.form.Checkbox.superclass.initEvents.call(this);
43244         // has no events...
43245        
43246     },
43247
43248
43249     getResizeEl : function(){
43250         return this.wrap;
43251     },
43252
43253     getPositionEl : function(){
43254         return this.wrap;
43255     },
43256
43257     // private
43258     onRender : function(ct, position){
43259         
43260         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
43261         var style = this.style;
43262         delete this.style;
43263         
43264         Roo.form.GridField.superclass.onRender.call(this, ct, position);
43265         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
43266         this.viewEl = this.wrap.createChild({ tag: 'div' });
43267         if (style) {
43268             this.viewEl.applyStyles(style);
43269         }
43270         if (this.width) {
43271             this.viewEl.setWidth(this.width);
43272         }
43273         if (this.height) {
43274             this.viewEl.setHeight(this.height);
43275         }
43276         //if(this.inputValue !== undefined){
43277         //this.setValue(this.value);
43278         
43279         
43280         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
43281         
43282         
43283         this.grid.render();
43284         this.grid.getDataSource().on('remove', this.refreshValue, this);
43285         this.grid.getDataSource().on('update', this.refreshValue, this);
43286         this.grid.on('afteredit', this.refreshValue, this);
43287  
43288     },
43289      
43290     
43291     /**
43292      * Sets the value of the item. 
43293      * @param {String} either an object  or a string..
43294      */
43295     setValue : function(v){
43296         //this.value = v;
43297         v = v || []; // empty set..
43298         // this does not seem smart - it really only affects memoryproxy grids..
43299         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
43300             var ds = this.grid.getDataSource();
43301             // assumes a json reader..
43302             var data = {}
43303             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
43304             ds.loadData( data);
43305         }
43306         // clear selection so it does not get stale.
43307         if (this.grid.sm) { 
43308             this.grid.sm.clearSelections();
43309         }
43310         
43311         Roo.form.GridField.superclass.setValue.call(this, v);
43312         this.refreshValue();
43313         // should load data in the grid really....
43314     },
43315     
43316     // private
43317     refreshValue: function() {
43318          var val = [];
43319         this.grid.getDataSource().each(function(r) {
43320             val.push(r.data);
43321         });
43322         this.el.dom.value = Roo.encode(val);
43323     }
43324     
43325      
43326     
43327     
43328 });/*
43329  * Based on:
43330  * Ext JS Library 1.1.1
43331  * Copyright(c) 2006-2007, Ext JS, LLC.
43332  *
43333  * Originally Released Under LGPL - original licence link has changed is not relivant.
43334  *
43335  * Fork - LGPL
43336  * <script type="text/javascript">
43337  */
43338 /**
43339  * @class Roo.form.DisplayField
43340  * @extends Roo.form.Field
43341  * A generic Field to display non-editable data.
43342  * @constructor
43343  * Creates a new Display Field item.
43344  * @param {Object} config Configuration options
43345  */
43346 Roo.form.DisplayField = function(config){
43347     Roo.form.DisplayField.superclass.constructor.call(this, config);
43348     
43349 };
43350
43351 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
43352     inputType:      'hidden',
43353     allowBlank:     true,
43354     readOnly:         true,
43355     
43356  
43357     /**
43358      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
43359      */
43360     focusClass : undefined,
43361     /**
43362      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
43363      */
43364     fieldClass: 'x-form-field',
43365     
43366      /**
43367      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
43368      */
43369     valueRenderer: undefined,
43370     
43371     width: 100,
43372     /**
43373      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
43374      * {tag: "input", type: "checkbox", autocomplete: "off"})
43375      */
43376      
43377  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
43378
43379     onResize : function(){
43380         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
43381         
43382     },
43383
43384     initEvents : function(){
43385         // Roo.form.Checkbox.superclass.initEvents.call(this);
43386         // has no events...
43387        
43388     },
43389
43390
43391     getResizeEl : function(){
43392         return this.wrap;
43393     },
43394
43395     getPositionEl : function(){
43396         return this.wrap;
43397     },
43398
43399     // private
43400     onRender : function(ct, position){
43401         
43402         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
43403         //if(this.inputValue !== undefined){
43404         this.wrap = this.el.wrap();
43405         
43406         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
43407         
43408         if (this.bodyStyle) {
43409             this.viewEl.applyStyles(this.bodyStyle);
43410         }
43411         //this.viewEl.setStyle('padding', '2px');
43412         
43413         this.setValue(this.value);
43414         
43415     },
43416 /*
43417     // private
43418     initValue : Roo.emptyFn,
43419
43420   */
43421
43422         // private
43423     onClick : function(){
43424         
43425     },
43426
43427     /**
43428      * Sets the checked state of the checkbox.
43429      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
43430      */
43431     setValue : function(v){
43432         this.value = v;
43433         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
43434         // this might be called before we have a dom element..
43435         if (!this.viewEl) {
43436             return;
43437         }
43438         this.viewEl.dom.innerHTML = html;
43439         Roo.form.DisplayField.superclass.setValue.call(this, v);
43440
43441     }
43442 });/*
43443  * 
43444  * Licence- LGPL
43445  * 
43446  */
43447
43448 /**
43449  * @class Roo.form.DayPicker
43450  * @extends Roo.form.Field
43451  * A Day picker show [M] [T] [W] ....
43452  * @constructor
43453  * Creates a new Day Picker
43454  * @param {Object} config Configuration options
43455  */
43456 Roo.form.DayPicker= function(config){
43457     Roo.form.DayPicker.superclass.constructor.call(this, config);
43458      
43459 };
43460
43461 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
43462     /**
43463      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
43464      */
43465     focusClass : undefined,
43466     /**
43467      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
43468      */
43469     fieldClass: "x-form-field",
43470    
43471     /**
43472      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
43473      * {tag: "input", type: "checkbox", autocomplete: "off"})
43474      */
43475     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
43476     
43477    
43478     actionMode : 'viewEl', 
43479     //
43480     // private
43481  
43482     inputType : 'hidden',
43483     
43484      
43485     inputElement: false, // real input element?
43486     basedOn: false, // ????
43487     
43488     isFormField: true, // not sure where this is needed!!!!
43489
43490     onResize : function(){
43491         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
43492         if(!this.boxLabel){
43493             this.el.alignTo(this.wrap, 'c-c');
43494         }
43495     },
43496
43497     initEvents : function(){
43498         Roo.form.Checkbox.superclass.initEvents.call(this);
43499         this.el.on("click", this.onClick,  this);
43500         this.el.on("change", this.onClick,  this);
43501     },
43502
43503
43504     getResizeEl : function(){
43505         return this.wrap;
43506     },
43507
43508     getPositionEl : function(){
43509         return this.wrap;
43510     },
43511
43512     
43513     // private
43514     onRender : function(ct, position){
43515         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
43516        
43517         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
43518         
43519         var r1 = '<table><tr>';
43520         var r2 = '<tr class="x-form-daypick-icons">';
43521         for (var i=0; i < 7; i++) {
43522             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
43523             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
43524         }
43525         
43526         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
43527         viewEl.select('img').on('click', this.onClick, this);
43528         this.viewEl = viewEl;   
43529         
43530         
43531         // this will not work on Chrome!!!
43532         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
43533         this.el.on('propertychange', this.setFromHidden,  this);  //ie
43534         
43535         
43536           
43537
43538     },
43539
43540     // private
43541     initValue : Roo.emptyFn,
43542
43543     /**
43544      * Returns the checked state of the checkbox.
43545      * @return {Boolean} True if checked, else false
43546      */
43547     getValue : function(){
43548         return this.el.dom.value;
43549         
43550     },
43551
43552         // private
43553     onClick : function(e){ 
43554         //this.setChecked(!this.checked);
43555         Roo.get(e.target).toggleClass('x-menu-item-checked');
43556         this.refreshValue();
43557         //if(this.el.dom.checked != this.checked){
43558         //    this.setValue(this.el.dom.checked);
43559        // }
43560     },
43561     
43562     // private
43563     refreshValue : function()
43564     {
43565         var val = '';
43566         this.viewEl.select('img',true).each(function(e,i,n)  {
43567             val += e.is(".x-menu-item-checked") ? String(n) : '';
43568         });
43569         this.setValue(val, true);
43570     },
43571
43572     /**
43573      * Sets the checked state of the checkbox.
43574      * On is always based on a string comparison between inputValue and the param.
43575      * @param {Boolean/String} value - the value to set 
43576      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
43577      */
43578     setValue : function(v,suppressEvent){
43579         if (!this.el.dom) {
43580             return;
43581         }
43582         var old = this.el.dom.value ;
43583         this.el.dom.value = v;
43584         if (suppressEvent) {
43585             return ;
43586         }
43587          
43588         // update display..
43589         this.viewEl.select('img',true).each(function(e,i,n)  {
43590             
43591             var on = e.is(".x-menu-item-checked");
43592             var newv = v.indexOf(String(n)) > -1;
43593             if (on != newv) {
43594                 e.toggleClass('x-menu-item-checked');
43595             }
43596             
43597         });
43598         
43599         
43600         this.fireEvent('change', this, v, old);
43601         
43602         
43603     },
43604    
43605     // handle setting of hidden value by some other method!!?!?
43606     setFromHidden: function()
43607     {
43608         if(!this.el){
43609             return;
43610         }
43611         //console.log("SET FROM HIDDEN");
43612         //alert('setFrom hidden');
43613         this.setValue(this.el.dom.value);
43614     },
43615     
43616     onDestroy : function()
43617     {
43618         if(this.viewEl){
43619             Roo.get(this.viewEl).remove();
43620         }
43621          
43622         Roo.form.DayPicker.superclass.onDestroy.call(this);
43623     }
43624
43625 });/*
43626  * RooJS Library 1.1.1
43627  * Copyright(c) 2008-2011  Alan Knowles
43628  *
43629  * License - LGPL
43630  */
43631  
43632
43633 /**
43634  * @class Roo.form.ComboCheck
43635  * @extends Roo.form.ComboBox
43636  * A combobox for multiple select items.
43637  *
43638  * FIXME - could do with a reset button..
43639  * 
43640  * @constructor
43641  * Create a new ComboCheck
43642  * @param {Object} config Configuration options
43643  */
43644 Roo.form.ComboCheck = function(config){
43645     Roo.form.ComboCheck.superclass.constructor.call(this, config);
43646     // should verify some data...
43647     // like
43648     // hiddenName = required..
43649     // displayField = required
43650     // valudField == required
43651     var req= [ 'hiddenName', 'displayField', 'valueField' ];
43652     var _t = this;
43653     Roo.each(req, function(e) {
43654         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
43655             throw "Roo.form.ComboCheck : missing value for: " + e;
43656         }
43657     });
43658     
43659     
43660 };
43661
43662 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
43663      
43664      
43665     editable : false,
43666      
43667     selectedClass: 'x-menu-item-checked', 
43668     
43669     // private
43670     onRender : function(ct, position){
43671         var _t = this;
43672         
43673         
43674         
43675         if(!this.tpl){
43676             var cls = 'x-combo-list';
43677
43678             
43679             this.tpl =  new Roo.Template({
43680                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
43681                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
43682                    '<span>{' + this.displayField + '}</span>' +
43683                     '</div>' 
43684                 
43685             });
43686         }
43687  
43688         
43689         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
43690         this.view.singleSelect = false;
43691         this.view.multiSelect = true;
43692         this.view.toggleSelect = true;
43693         this.pageTb.add(new Roo.Toolbar.Fill(), {
43694             
43695             text: 'Done',
43696             handler: function()
43697             {
43698                 _t.collapse();
43699             }
43700         });
43701     },
43702     
43703     onViewOver : function(e, t){
43704         // do nothing...
43705         return;
43706         
43707     },
43708     
43709     onViewClick : function(doFocus,index){
43710         return;
43711         
43712     },
43713     select: function () {
43714         //Roo.log("SELECT CALLED");
43715     },
43716      
43717     selectByValue : function(xv, scrollIntoView){
43718         var ar = this.getValueArray();
43719         var sels = [];
43720         
43721         Roo.each(ar, function(v) {
43722             if(v === undefined || v === null){
43723                 return;
43724             }
43725             var r = this.findRecord(this.valueField, v);
43726             if(r){
43727                 sels.push(this.store.indexOf(r))
43728                 
43729             }
43730         },this);
43731         this.view.select(sels);
43732         return false;
43733     },
43734     
43735     
43736     
43737     onSelect : function(record, index){
43738        // Roo.log("onselect Called");
43739        // this is only called by the clear button now..
43740         this.view.clearSelections();
43741         this.setValue('[]');
43742         if (this.value != this.valueBefore) {
43743             this.fireEvent('change', this, this.value, this.valueBefore);
43744         }
43745     },
43746     getValueArray : function()
43747     {
43748         var ar = [] ;
43749         
43750         try {
43751             //Roo.log(this.value);
43752             if (typeof(this.value) == 'undefined') {
43753                 return [];
43754             }
43755             var ar = Roo.decode(this.value);
43756             return  ar instanceof Array ? ar : []; //?? valid?
43757             
43758         } catch(e) {
43759             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
43760             return [];
43761         }
43762          
43763     },
43764     expand : function ()
43765     {
43766         Roo.form.ComboCheck.superclass.expand.call(this);
43767         this.valueBefore = this.value;
43768         
43769
43770     },
43771     
43772     collapse : function(){
43773         Roo.form.ComboCheck.superclass.collapse.call(this);
43774         var sl = this.view.getSelectedIndexes();
43775         var st = this.store;
43776         var nv = [];
43777         var tv = [];
43778         var r;
43779         Roo.each(sl, function(i) {
43780             r = st.getAt(i);
43781             nv.push(r.get(this.valueField));
43782         },this);
43783         this.setValue(Roo.encode(nv));
43784         if (this.value != this.valueBefore) {
43785
43786             this.fireEvent('change', this, this.value, this.valueBefore);
43787         }
43788         
43789     },
43790     
43791     setValue : function(v){
43792         // Roo.log(v);
43793         this.value = v;
43794         
43795         var vals = this.getValueArray();
43796         var tv = [];
43797         Roo.each(vals, function(k) {
43798             var r = this.findRecord(this.valueField, k);
43799             if(r){
43800                 tv.push(r.data[this.displayField]);
43801             }else if(this.valueNotFoundText !== undefined){
43802                 tv.push( this.valueNotFoundText );
43803             }
43804         },this);
43805        // Roo.log(tv);
43806         
43807         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
43808         this.hiddenField.value = v;
43809         this.value = v;
43810     }
43811     
43812 });//<script type="text/javasscript">
43813  
43814
43815 /**
43816  * @class Roo.DDView
43817  * A DnD enabled version of Roo.View.
43818  * @param {Element/String} container The Element in which to create the View.
43819  * @param {String} tpl The template string used to create the markup for each element of the View
43820  * @param {Object} config The configuration properties. These include all the config options of
43821  * {@link Roo.View} plus some specific to this class.<br>
43822  * <p>
43823  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
43824  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
43825  * <p>
43826  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
43827 .x-view-drag-insert-above {
43828         border-top:1px dotted #3366cc;
43829 }
43830 .x-view-drag-insert-below {
43831         border-bottom:1px dotted #3366cc;
43832 }
43833 </code></pre>
43834  * 
43835  */
43836  
43837 Roo.DDView = function(container, tpl, config) {
43838     Roo.DDView.superclass.constructor.apply(this, arguments);
43839     this.getEl().setStyle("outline", "0px none");
43840     this.getEl().unselectable();
43841     if (this.dragGroup) {
43842                 this.setDraggable(this.dragGroup.split(","));
43843     }
43844     if (this.dropGroup) {
43845                 this.setDroppable(this.dropGroup.split(","));
43846     }
43847     if (this.deletable) {
43848         this.setDeletable();
43849     }
43850     this.isDirtyFlag = false;
43851         this.addEvents({
43852                 "drop" : true
43853         });
43854 };
43855
43856 Roo.extend(Roo.DDView, Roo.View, {
43857 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
43858 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
43859 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
43860 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
43861
43862         isFormField: true,
43863
43864         reset: Roo.emptyFn,
43865         
43866         clearInvalid: Roo.form.Field.prototype.clearInvalid,
43867
43868         validate: function() {
43869                 return true;
43870         },
43871         
43872         destroy: function() {
43873                 this.purgeListeners();
43874                 this.getEl.removeAllListeners();
43875                 this.getEl().remove();
43876                 if (this.dragZone) {
43877                         if (this.dragZone.destroy) {
43878                                 this.dragZone.destroy();
43879                         }
43880                 }
43881                 if (this.dropZone) {
43882                         if (this.dropZone.destroy) {
43883                                 this.dropZone.destroy();
43884                         }
43885                 }
43886         },
43887
43888 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
43889         getName: function() {
43890                 return this.name;
43891         },
43892
43893 /**     Loads the View from a JSON string representing the Records to put into the Store. */
43894         setValue: function(v) {
43895                 if (!this.store) {
43896                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
43897                 }
43898                 var data = {};
43899                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
43900                 this.store.proxy = new Roo.data.MemoryProxy(data);
43901                 this.store.load();
43902         },
43903
43904 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
43905         getValue: function() {
43906                 var result = '(';
43907                 this.store.each(function(rec) {
43908                         result += rec.id + ',';
43909                 });
43910                 return result.substr(0, result.length - 1) + ')';
43911         },
43912         
43913         getIds: function() {
43914                 var i = 0, result = new Array(this.store.getCount());
43915                 this.store.each(function(rec) {
43916                         result[i++] = rec.id;
43917                 });
43918                 return result;
43919         },
43920         
43921         isDirty: function() {
43922                 return this.isDirtyFlag;
43923         },
43924
43925 /**
43926  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
43927  *      whole Element becomes the target, and this causes the drop gesture to append.
43928  */
43929     getTargetFromEvent : function(e) {
43930                 var target = e.getTarget();
43931                 while ((target !== null) && (target.parentNode != this.el.dom)) {
43932                 target = target.parentNode;
43933                 }
43934                 if (!target) {
43935                         target = this.el.dom.lastChild || this.el.dom;
43936                 }
43937                 return target;
43938     },
43939
43940 /**
43941  *      Create the drag data which consists of an object which has the property "ddel" as
43942  *      the drag proxy element. 
43943  */
43944     getDragData : function(e) {
43945         var target = this.findItemFromChild(e.getTarget());
43946                 if(target) {
43947                         this.handleSelection(e);
43948                         var selNodes = this.getSelectedNodes();
43949             var dragData = {
43950                 source: this,
43951                 copy: this.copy || (this.allowCopy && e.ctrlKey),
43952                 nodes: selNodes,
43953                 records: []
43954                         };
43955                         var selectedIndices = this.getSelectedIndexes();
43956                         for (var i = 0; i < selectedIndices.length; i++) {
43957                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
43958                         }
43959                         if (selNodes.length == 1) {
43960                                 dragData.ddel = target.cloneNode(true); // the div element
43961                         } else {
43962                                 var div = document.createElement('div'); // create the multi element drag "ghost"
43963                                 div.className = 'multi-proxy';
43964                                 for (var i = 0, len = selNodes.length; i < len; i++) {
43965                                         div.appendChild(selNodes[i].cloneNode(true));
43966                                 }
43967                                 dragData.ddel = div;
43968                         }
43969             //console.log(dragData)
43970             //console.log(dragData.ddel.innerHTML)
43971                         return dragData;
43972                 }
43973         //console.log('nodragData')
43974                 return false;
43975     },
43976     
43977 /**     Specify to which ddGroup items in this DDView may be dragged. */
43978     setDraggable: function(ddGroup) {
43979         if (ddGroup instanceof Array) {
43980                 Roo.each(ddGroup, this.setDraggable, this);
43981                 return;
43982         }
43983         if (this.dragZone) {
43984                 this.dragZone.addToGroup(ddGroup);
43985         } else {
43986                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
43987                                 containerScroll: true,
43988                                 ddGroup: ddGroup 
43989
43990                         });
43991 //                      Draggability implies selection. DragZone's mousedown selects the element.
43992                         if (!this.multiSelect) { this.singleSelect = true; }
43993
43994 //                      Wire the DragZone's handlers up to methods in *this*
43995                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
43996                 }
43997     },
43998
43999 /**     Specify from which ddGroup this DDView accepts drops. */
44000     setDroppable: function(ddGroup) {
44001         if (ddGroup instanceof Array) {
44002                 Roo.each(ddGroup, this.setDroppable, this);
44003                 return;
44004         }
44005         if (this.dropZone) {
44006                 this.dropZone.addToGroup(ddGroup);
44007         } else {
44008                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
44009                                 containerScroll: true,
44010                                 ddGroup: ddGroup
44011                         });
44012
44013 //                      Wire the DropZone's handlers up to methods in *this*
44014                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
44015                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
44016                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
44017                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
44018                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
44019                 }
44020     },
44021
44022 /**     Decide whether to drop above or below a View node. */
44023     getDropPoint : function(e, n, dd){
44024         if (n == this.el.dom) { return "above"; }
44025                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
44026                 var c = t + (b - t) / 2;
44027                 var y = Roo.lib.Event.getPageY(e);
44028                 if(y <= c) {
44029                         return "above";
44030                 }else{
44031                         return "below";
44032                 }
44033     },
44034
44035     onNodeEnter : function(n, dd, e, data){
44036                 return false;
44037     },
44038     
44039     onNodeOver : function(n, dd, e, data){
44040                 var pt = this.getDropPoint(e, n, dd);
44041                 // set the insert point style on the target node
44042                 var dragElClass = this.dropNotAllowed;
44043                 if (pt) {
44044                         var targetElClass;
44045                         if (pt == "above"){
44046                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
44047                                 targetElClass = "x-view-drag-insert-above";
44048                         } else {
44049                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
44050                                 targetElClass = "x-view-drag-insert-below";
44051                         }
44052                         if (this.lastInsertClass != targetElClass){
44053                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
44054                                 this.lastInsertClass = targetElClass;
44055                         }
44056                 }
44057                 return dragElClass;
44058         },
44059
44060     onNodeOut : function(n, dd, e, data){
44061                 this.removeDropIndicators(n);
44062     },
44063
44064     onNodeDrop : function(n, dd, e, data){
44065         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
44066                 return false;
44067         }
44068         var pt = this.getDropPoint(e, n, dd);
44069                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
44070                 if (pt == "below") { insertAt++; }
44071                 for (var i = 0; i < data.records.length; i++) {
44072                         var r = data.records[i];
44073                         var dup = this.store.getById(r.id);
44074                         if (dup && (dd != this.dragZone)) {
44075                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
44076                         } else {
44077                                 if (data.copy) {
44078                                         this.store.insert(insertAt++, r.copy());
44079                                 } else {
44080                                         data.source.isDirtyFlag = true;
44081                                         r.store.remove(r);
44082                                         this.store.insert(insertAt++, r);
44083                                 }
44084                                 this.isDirtyFlag = true;
44085                         }
44086                 }
44087                 this.dragZone.cachedTarget = null;
44088                 return true;
44089     },
44090
44091     removeDropIndicators : function(n){
44092                 if(n){
44093                         Roo.fly(n).removeClass([
44094                                 "x-view-drag-insert-above",
44095                                 "x-view-drag-insert-below"]);
44096                         this.lastInsertClass = "_noclass";
44097                 }
44098     },
44099
44100 /**
44101  *      Utility method. Add a delete option to the DDView's context menu.
44102  *      @param {String} imageUrl The URL of the "delete" icon image.
44103  */
44104         setDeletable: function(imageUrl) {
44105                 if (!this.singleSelect && !this.multiSelect) {
44106                         this.singleSelect = true;
44107                 }
44108                 var c = this.getContextMenu();
44109                 this.contextMenu.on("itemclick", function(item) {
44110                         switch (item.id) {
44111                                 case "delete":
44112                                         this.remove(this.getSelectedIndexes());
44113                                         break;
44114                         }
44115                 }, this);
44116                 this.contextMenu.add({
44117                         icon: imageUrl,
44118                         id: "delete",
44119                         text: 'Delete'
44120                 });
44121         },
44122         
44123 /**     Return the context menu for this DDView. */
44124         getContextMenu: function() {
44125                 if (!this.contextMenu) {
44126 //                      Create the View's context menu
44127                         this.contextMenu = new Roo.menu.Menu({
44128                                 id: this.id + "-contextmenu"
44129                         });
44130                         this.el.on("contextmenu", this.showContextMenu, this);
44131                 }
44132                 return this.contextMenu;
44133         },
44134         
44135         disableContextMenu: function() {
44136                 if (this.contextMenu) {
44137                         this.el.un("contextmenu", this.showContextMenu, this);
44138                 }
44139         },
44140
44141         showContextMenu: function(e, item) {
44142         item = this.findItemFromChild(e.getTarget());
44143                 if (item) {
44144                         e.stopEvent();
44145                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
44146                         this.contextMenu.showAt(e.getXY());
44147             }
44148     },
44149
44150 /**
44151  *      Remove {@link Roo.data.Record}s at the specified indices.
44152  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
44153  */
44154     remove: function(selectedIndices) {
44155                 selectedIndices = [].concat(selectedIndices);
44156                 for (var i = 0; i < selectedIndices.length; i++) {
44157                         var rec = this.store.getAt(selectedIndices[i]);
44158                         this.store.remove(rec);
44159                 }
44160     },
44161
44162 /**
44163  *      Double click fires the event, but also, if this is draggable, and there is only one other
44164  *      related DropZone, it transfers the selected node.
44165  */
44166     onDblClick : function(e){
44167         var item = this.findItemFromChild(e.getTarget());
44168         if(item){
44169             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
44170                 return false;
44171             }
44172             if (this.dragGroup) {
44173                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
44174                     while (targets.indexOf(this.dropZone) > -1) {
44175                             targets.remove(this.dropZone);
44176                                 }
44177                     if (targets.length == 1) {
44178                                         this.dragZone.cachedTarget = null;
44179                         var el = Roo.get(targets[0].getEl());
44180                         var box = el.getBox(true);
44181                         targets[0].onNodeDrop(el.dom, {
44182                                 target: el.dom,
44183                                 xy: [box.x, box.y + box.height - 1]
44184                         }, null, this.getDragData(e));
44185                     }
44186                 }
44187         }
44188     },
44189     
44190     handleSelection: function(e) {
44191                 this.dragZone.cachedTarget = null;
44192         var item = this.findItemFromChild(e.getTarget());
44193         if (!item) {
44194                 this.clearSelections(true);
44195                 return;
44196         }
44197                 if (item && (this.multiSelect || this.singleSelect)){
44198                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
44199                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
44200                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
44201                                 this.unselect(item);
44202                         } else {
44203                                 this.select(item, this.multiSelect && e.ctrlKey);
44204                                 this.lastSelection = item;
44205                         }
44206                 }
44207     },
44208
44209     onItemClick : function(item, index, e){
44210                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
44211                         return false;
44212                 }
44213                 return true;
44214     },
44215
44216     unselect : function(nodeInfo, suppressEvent){
44217                 var node = this.getNode(nodeInfo);
44218                 if(node && this.isSelected(node)){
44219                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
44220                                 Roo.fly(node).removeClass(this.selectedClass);
44221                                 this.selections.remove(node);
44222                                 if(!suppressEvent){
44223                                         this.fireEvent("selectionchange", this, this.selections);
44224                                 }
44225                         }
44226                 }
44227     }
44228 });
44229 /*
44230  * Based on:
44231  * Ext JS Library 1.1.1
44232  * Copyright(c) 2006-2007, Ext JS, LLC.
44233  *
44234  * Originally Released Under LGPL - original licence link has changed is not relivant.
44235  *
44236  * Fork - LGPL
44237  * <script type="text/javascript">
44238  */
44239  
44240 /**
44241  * @class Roo.LayoutManager
44242  * @extends Roo.util.Observable
44243  * Base class for layout managers.
44244  */
44245 Roo.LayoutManager = function(container, config){
44246     Roo.LayoutManager.superclass.constructor.call(this);
44247     this.el = Roo.get(container);
44248     // ie scrollbar fix
44249     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
44250         document.body.scroll = "no";
44251     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
44252         this.el.position('relative');
44253     }
44254     this.id = this.el.id;
44255     this.el.addClass("x-layout-container");
44256     /** false to disable window resize monitoring @type Boolean */
44257     this.monitorWindowResize = true;
44258     this.regions = {};
44259     this.addEvents({
44260         /**
44261          * @event layout
44262          * Fires when a layout is performed. 
44263          * @param {Roo.LayoutManager} this
44264          */
44265         "layout" : true,
44266         /**
44267          * @event regionresized
44268          * Fires when the user resizes a region. 
44269          * @param {Roo.LayoutRegion} region The resized region
44270          * @param {Number} newSize The new size (width for east/west, height for north/south)
44271          */
44272         "regionresized" : true,
44273         /**
44274          * @event regioncollapsed
44275          * Fires when a region is collapsed. 
44276          * @param {Roo.LayoutRegion} region The collapsed region
44277          */
44278         "regioncollapsed" : true,
44279         /**
44280          * @event regionexpanded
44281          * Fires when a region is expanded.  
44282          * @param {Roo.LayoutRegion} region The expanded region
44283          */
44284         "regionexpanded" : true
44285     });
44286     this.updating = false;
44287     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
44288 };
44289
44290 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
44291     /**
44292      * Returns true if this layout is currently being updated
44293      * @return {Boolean}
44294      */
44295     isUpdating : function(){
44296         return this.updating; 
44297     },
44298     
44299     /**
44300      * Suspend the LayoutManager from doing auto-layouts while
44301      * making multiple add or remove calls
44302      */
44303     beginUpdate : function(){
44304         this.updating = true;    
44305     },
44306     
44307     /**
44308      * Restore auto-layouts and optionally disable the manager from performing a layout
44309      * @param {Boolean} noLayout true to disable a layout update 
44310      */
44311     endUpdate : function(noLayout){
44312         this.updating = false;
44313         if(!noLayout){
44314             this.layout();
44315         }    
44316     },
44317     
44318     layout: function(){
44319         
44320     },
44321     
44322     onRegionResized : function(region, newSize){
44323         this.fireEvent("regionresized", region, newSize);
44324         this.layout();
44325     },
44326     
44327     onRegionCollapsed : function(region){
44328         this.fireEvent("regioncollapsed", region);
44329     },
44330     
44331     onRegionExpanded : function(region){
44332         this.fireEvent("regionexpanded", region);
44333     },
44334         
44335     /**
44336      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
44337      * performs box-model adjustments.
44338      * @return {Object} The size as an object {width: (the width), height: (the height)}
44339      */
44340     getViewSize : function(){
44341         var size;
44342         if(this.el.dom != document.body){
44343             size = this.el.getSize();
44344         }else{
44345             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
44346         }
44347         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
44348         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
44349         return size;
44350     },
44351     
44352     /**
44353      * Returns the Element this layout is bound to.
44354      * @return {Roo.Element}
44355      */
44356     getEl : function(){
44357         return this.el;
44358     },
44359     
44360     /**
44361      * Returns the specified region.
44362      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
44363      * @return {Roo.LayoutRegion}
44364      */
44365     getRegion : function(target){
44366         return this.regions[target.toLowerCase()];
44367     },
44368     
44369     onWindowResize : function(){
44370         if(this.monitorWindowResize){
44371             this.layout();
44372         }
44373     }
44374 });/*
44375  * Based on:
44376  * Ext JS Library 1.1.1
44377  * Copyright(c) 2006-2007, Ext JS, LLC.
44378  *
44379  * Originally Released Under LGPL - original licence link has changed is not relivant.
44380  *
44381  * Fork - LGPL
44382  * <script type="text/javascript">
44383  */
44384 /**
44385  * @class Roo.BorderLayout
44386  * @extends Roo.LayoutManager
44387  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
44388  * please see: <br><br>
44389  * <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>
44390  * <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>
44391  * Example:
44392  <pre><code>
44393  var layout = new Roo.BorderLayout(document.body, {
44394     north: {
44395         initialSize: 25,
44396         titlebar: false
44397     },
44398     west: {
44399         split:true,
44400         initialSize: 200,
44401         minSize: 175,
44402         maxSize: 400,
44403         titlebar: true,
44404         collapsible: true
44405     },
44406     east: {
44407         split:true,
44408         initialSize: 202,
44409         minSize: 175,
44410         maxSize: 400,
44411         titlebar: true,
44412         collapsible: true
44413     },
44414     south: {
44415         split:true,
44416         initialSize: 100,
44417         minSize: 100,
44418         maxSize: 200,
44419         titlebar: true,
44420         collapsible: true
44421     },
44422     center: {
44423         titlebar: true,
44424         autoScroll:true,
44425         resizeTabs: true,
44426         minTabWidth: 50,
44427         preferredTabWidth: 150
44428     }
44429 });
44430
44431 // shorthand
44432 var CP = Roo.ContentPanel;
44433
44434 layout.beginUpdate();
44435 layout.add("north", new CP("north", "North"));
44436 layout.add("south", new CP("south", {title: "South", closable: true}));
44437 layout.add("west", new CP("west", {title: "West"}));
44438 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
44439 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
44440 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
44441 layout.getRegion("center").showPanel("center1");
44442 layout.endUpdate();
44443 </code></pre>
44444
44445 <b>The container the layout is rendered into can be either the body element or any other element.
44446 If it is not the body element, the container needs to either be an absolute positioned element,
44447 or you will need to add "position:relative" to the css of the container.  You will also need to specify
44448 the container size if it is not the body element.</b>
44449
44450 * @constructor
44451 * Create a new BorderLayout
44452 * @param {String/HTMLElement/Element} container The container this layout is bound to
44453 * @param {Object} config Configuration options
44454  */
44455 Roo.BorderLayout = function(container, config){
44456     config = config || {};
44457     Roo.BorderLayout.superclass.constructor.call(this, container, config);
44458     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
44459     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
44460         var target = this.factory.validRegions[i];
44461         if(config[target]){
44462             this.addRegion(target, config[target]);
44463         }
44464     }
44465 };
44466
44467 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
44468     /**
44469      * Creates and adds a new region if it doesn't already exist.
44470      * @param {String} target The target region key (north, south, east, west or center).
44471      * @param {Object} config The regions config object
44472      * @return {BorderLayoutRegion} The new region
44473      */
44474     addRegion : function(target, config){
44475         if(!this.regions[target]){
44476             var r = this.factory.create(target, this, config);
44477             this.bindRegion(target, r);
44478         }
44479         return this.regions[target];
44480     },
44481
44482     // private (kinda)
44483     bindRegion : function(name, r){
44484         this.regions[name] = r;
44485         r.on("visibilitychange", this.layout, this);
44486         r.on("paneladded", this.layout, this);
44487         r.on("panelremoved", this.layout, this);
44488         r.on("invalidated", this.layout, this);
44489         r.on("resized", this.onRegionResized, this);
44490         r.on("collapsed", this.onRegionCollapsed, this);
44491         r.on("expanded", this.onRegionExpanded, this);
44492     },
44493
44494     /**
44495      * Performs a layout update.
44496      */
44497     layout : function(){
44498         if(this.updating) return;
44499         var size = this.getViewSize();
44500         var w = size.width;
44501         var h = size.height;
44502         var centerW = w;
44503         var centerH = h;
44504         var centerY = 0;
44505         var centerX = 0;
44506         //var x = 0, y = 0;
44507
44508         var rs = this.regions;
44509         var north = rs["north"];
44510         var south = rs["south"]; 
44511         var west = rs["west"];
44512         var east = rs["east"];
44513         var center = rs["center"];
44514         //if(this.hideOnLayout){ // not supported anymore
44515             //c.el.setStyle("display", "none");
44516         //}
44517         if(north && north.isVisible()){
44518             var b = north.getBox();
44519             var m = north.getMargins();
44520             b.width = w - (m.left+m.right);
44521             b.x = m.left;
44522             b.y = m.top;
44523             centerY = b.height + b.y + m.bottom;
44524             centerH -= centerY;
44525             north.updateBox(this.safeBox(b));
44526         }
44527         if(south && south.isVisible()){
44528             var b = south.getBox();
44529             var m = south.getMargins();
44530             b.width = w - (m.left+m.right);
44531             b.x = m.left;
44532             var totalHeight = (b.height + m.top + m.bottom);
44533             b.y = h - totalHeight + m.top;
44534             centerH -= totalHeight;
44535             south.updateBox(this.safeBox(b));
44536         }
44537         if(west && west.isVisible()){
44538             var b = west.getBox();
44539             var m = west.getMargins();
44540             b.height = centerH - (m.top+m.bottom);
44541             b.x = m.left;
44542             b.y = centerY + m.top;
44543             var totalWidth = (b.width + m.left + m.right);
44544             centerX += totalWidth;
44545             centerW -= totalWidth;
44546             west.updateBox(this.safeBox(b));
44547         }
44548         if(east && east.isVisible()){
44549             var b = east.getBox();
44550             var m = east.getMargins();
44551             b.height = centerH - (m.top+m.bottom);
44552             var totalWidth = (b.width + m.left + m.right);
44553             b.x = w - totalWidth + m.left;
44554             b.y = centerY + m.top;
44555             centerW -= totalWidth;
44556             east.updateBox(this.safeBox(b));
44557         }
44558         if(center){
44559             var m = center.getMargins();
44560             var centerBox = {
44561                 x: centerX + m.left,
44562                 y: centerY + m.top,
44563                 width: centerW - (m.left+m.right),
44564                 height: centerH - (m.top+m.bottom)
44565             };
44566             //if(this.hideOnLayout){
44567                 //center.el.setStyle("display", "block");
44568             //}
44569             center.updateBox(this.safeBox(centerBox));
44570         }
44571         this.el.repaint();
44572         this.fireEvent("layout", this);
44573     },
44574
44575     // private
44576     safeBox : function(box){
44577         box.width = Math.max(0, box.width);
44578         box.height = Math.max(0, box.height);
44579         return box;
44580     },
44581
44582     /**
44583      * Adds a ContentPanel (or subclass) to this layout.
44584      * @param {String} target The target region key (north, south, east, west or center).
44585      * @param {Roo.ContentPanel} panel The panel to add
44586      * @return {Roo.ContentPanel} The added panel
44587      */
44588     add : function(target, panel){
44589          
44590         target = target.toLowerCase();
44591         return this.regions[target].add(panel);
44592     },
44593
44594     /**
44595      * Remove a ContentPanel (or subclass) to this layout.
44596      * @param {String} target The target region key (north, south, east, west or center).
44597      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
44598      * @return {Roo.ContentPanel} The removed panel
44599      */
44600     remove : function(target, panel){
44601         target = target.toLowerCase();
44602         return this.regions[target].remove(panel);
44603     },
44604
44605     /**
44606      * Searches all regions for a panel with the specified id
44607      * @param {String} panelId
44608      * @return {Roo.ContentPanel} The panel or null if it wasn't found
44609      */
44610     findPanel : function(panelId){
44611         var rs = this.regions;
44612         for(var target in rs){
44613             if(typeof rs[target] != "function"){
44614                 var p = rs[target].getPanel(panelId);
44615                 if(p){
44616                     return p;
44617                 }
44618             }
44619         }
44620         return null;
44621     },
44622
44623     /**
44624      * Searches all regions for a panel with the specified id and activates (shows) it.
44625      * @param {String/ContentPanel} panelId The panels id or the panel itself
44626      * @return {Roo.ContentPanel} The shown panel or null
44627      */
44628     showPanel : function(panelId) {
44629       var rs = this.regions;
44630       for(var target in rs){
44631          var r = rs[target];
44632          if(typeof r != "function"){
44633             if(r.hasPanel(panelId)){
44634                return r.showPanel(panelId);
44635             }
44636          }
44637       }
44638       return null;
44639    },
44640
44641    /**
44642      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
44643      * @param {Roo.state.Provider} provider (optional) An alternate state provider
44644      */
44645     restoreState : function(provider){
44646         if(!provider){
44647             provider = Roo.state.Manager;
44648         }
44649         var sm = new Roo.LayoutStateManager();
44650         sm.init(this, provider);
44651     },
44652
44653     /**
44654      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
44655      * object should contain properties for each region to add ContentPanels to, and each property's value should be
44656      * a valid ContentPanel config object.  Example:
44657      * <pre><code>
44658 // Create the main layout
44659 var layout = new Roo.BorderLayout('main-ct', {
44660     west: {
44661         split:true,
44662         minSize: 175,
44663         titlebar: true
44664     },
44665     center: {
44666         title:'Components'
44667     }
44668 }, 'main-ct');
44669
44670 // Create and add multiple ContentPanels at once via configs
44671 layout.batchAdd({
44672    west: {
44673        id: 'source-files',
44674        autoCreate:true,
44675        title:'Ext Source Files',
44676        autoScroll:true,
44677        fitToFrame:true
44678    },
44679    center : {
44680        el: cview,
44681        autoScroll:true,
44682        fitToFrame:true,
44683        toolbar: tb,
44684        resizeEl:'cbody'
44685    }
44686 });
44687 </code></pre>
44688      * @param {Object} regions An object containing ContentPanel configs by region name
44689      */
44690     batchAdd : function(regions){
44691         this.beginUpdate();
44692         for(var rname in regions){
44693             var lr = this.regions[rname];
44694             if(lr){
44695                 this.addTypedPanels(lr, regions[rname]);
44696             }
44697         }
44698         this.endUpdate();
44699     },
44700
44701     // private
44702     addTypedPanels : function(lr, ps){
44703         if(typeof ps == 'string'){
44704             lr.add(new Roo.ContentPanel(ps));
44705         }
44706         else if(ps instanceof Array){
44707             for(var i =0, len = ps.length; i < len; i++){
44708                 this.addTypedPanels(lr, ps[i]);
44709             }
44710         }
44711         else if(!ps.events){ // raw config?
44712             var el = ps.el;
44713             delete ps.el; // prevent conflict
44714             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
44715         }
44716         else {  // panel object assumed!
44717             lr.add(ps);
44718         }
44719     },
44720     /**
44721      * Adds a xtype elements to the layout.
44722      * <pre><code>
44723
44724 layout.addxtype({
44725        xtype : 'ContentPanel',
44726        region: 'west',
44727        items: [ .... ]
44728    }
44729 );
44730
44731 layout.addxtype({
44732         xtype : 'NestedLayoutPanel',
44733         region: 'west',
44734         layout: {
44735            center: { },
44736            west: { }   
44737         },
44738         items : [ ... list of content panels or nested layout panels.. ]
44739    }
44740 );
44741 </code></pre>
44742      * @param {Object} cfg Xtype definition of item to add.
44743      */
44744     addxtype : function(cfg)
44745     {
44746         // basically accepts a pannel...
44747         // can accept a layout region..!?!?
44748         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
44749         
44750         if (!cfg.xtype.match(/Panel$/)) {
44751             return false;
44752         }
44753         var ret = false;
44754         
44755         if (typeof(cfg.region) == 'undefined') {
44756             Roo.log("Failed to add Panel, region was not set");
44757             Roo.log(cfg);
44758             return false;
44759         }
44760         var region = cfg.region;
44761         delete cfg.region;
44762         
44763           
44764         var xitems = [];
44765         if (cfg.items) {
44766             xitems = cfg.items;
44767             delete cfg.items;
44768         }
44769         var nb = false;
44770         
44771         switch(cfg.xtype) 
44772         {
44773             case 'ContentPanel':  // ContentPanel (el, cfg)
44774             case 'ScrollPanel':  // ContentPanel (el, cfg)
44775                 if(cfg.autoCreate) {
44776                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
44777                 } else {
44778                     var el = this.el.createChild();
44779                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
44780                 }
44781                 
44782                 this.add(region, ret);
44783                 break;
44784             
44785             
44786             case 'TreePanel': // our new panel!
44787                 cfg.el = this.el.createChild();
44788                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
44789                 this.add(region, ret);
44790                 break;
44791             
44792             case 'NestedLayoutPanel': 
44793                 // create a new Layout (which is  a Border Layout...
44794                 var el = this.el.createChild();
44795                 var clayout = cfg.layout;
44796                 delete cfg.layout;
44797                 clayout.items   = clayout.items  || [];
44798                 // replace this exitems with the clayout ones..
44799                 xitems = clayout.items;
44800                  
44801                 
44802                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
44803                     cfg.background = false;
44804                 }
44805                 var layout = new Roo.BorderLayout(el, clayout);
44806                 
44807                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
44808                 //console.log('adding nested layout panel '  + cfg.toSource());
44809                 this.add(region, ret);
44810                 nb = {}; /// find first...
44811                 break;
44812                 
44813             case 'GridPanel': 
44814             
44815                 // needs grid and region
44816                 
44817                 //var el = this.getRegion(region).el.createChild();
44818                 var el = this.el.createChild();
44819                 // create the grid first...
44820                 
44821                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
44822                 delete cfg.grid;
44823                 if (region == 'center' && this.active ) {
44824                     cfg.background = false;
44825                 }
44826                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
44827                 
44828                 this.add(region, ret);
44829                 if (cfg.background) {
44830                     ret.on('activate', function(gp) {
44831                         if (!gp.grid.rendered) {
44832                             gp.grid.render();
44833                         }
44834                     });
44835                 } else {
44836                     grid.render();
44837                 }
44838                 break;
44839            
44840                
44841                 
44842                 
44843             default: 
44844                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
44845                 return null;
44846              // GridPanel (grid, cfg)
44847             
44848         }
44849         this.beginUpdate();
44850         // add children..
44851         var region = '';
44852         Roo.each(xitems, function(i)  {
44853             region = nb && i.region && !i.background ? i.region : false;
44854             
44855             var add = ret.addxtype(i);
44856            
44857             if (region) {
44858                 nb[region] = add;
44859             }
44860             
44861         });
44862         this.endUpdate();
44863         // make the last non-background panel active..
44864         //if (nb) { Roo.log(nb); }
44865         if (nb) {
44866             
44867             for(var r in nb) {
44868                 region = this.getRegion(r);
44869                 if (region) {
44870                     region.setActivePanel(nb[r]);
44871                 }
44872             }
44873         }
44874         return ret;
44875         
44876     }
44877 });
44878
44879 /**
44880  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
44881  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
44882  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
44883  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
44884  * <pre><code>
44885 // shorthand
44886 var CP = Roo.ContentPanel;
44887
44888 var layout = Roo.BorderLayout.create({
44889     north: {
44890         initialSize: 25,
44891         titlebar: false,
44892         panels: [new CP("north", "North")]
44893     },
44894     west: {
44895         split:true,
44896         initialSize: 200,
44897         minSize: 175,
44898         maxSize: 400,
44899         titlebar: true,
44900         collapsible: true,
44901         panels: [new CP("west", {title: "West"})]
44902     },
44903     east: {
44904         split:true,
44905         initialSize: 202,
44906         minSize: 175,
44907         maxSize: 400,
44908         titlebar: true,
44909         collapsible: true,
44910         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
44911     },
44912     south: {
44913         split:true,
44914         initialSize: 100,
44915         minSize: 100,
44916         maxSize: 200,
44917         titlebar: true,
44918         collapsible: true,
44919         panels: [new CP("south", {title: "South", closable: true})]
44920     },
44921     center: {
44922         titlebar: true,
44923         autoScroll:true,
44924         resizeTabs: true,
44925         minTabWidth: 50,
44926         preferredTabWidth: 150,
44927         panels: [
44928             new CP("center1", {title: "Close Me", closable: true}),
44929             new CP("center2", {title: "Center Panel", closable: false})
44930         ]
44931     }
44932 }, document.body);
44933
44934 layout.getRegion("center").showPanel("center1");
44935 </code></pre>
44936  * @param config
44937  * @param targetEl
44938  */
44939 Roo.BorderLayout.create = function(config, targetEl){
44940     var layout = new Roo.BorderLayout(targetEl || document.body, config);
44941     layout.beginUpdate();
44942     var regions = Roo.BorderLayout.RegionFactory.validRegions;
44943     for(var j = 0, jlen = regions.length; j < jlen; j++){
44944         var lr = regions[j];
44945         if(layout.regions[lr] && config[lr].panels){
44946             var r = layout.regions[lr];
44947             var ps = config[lr].panels;
44948             layout.addTypedPanels(r, ps);
44949         }
44950     }
44951     layout.endUpdate();
44952     return layout;
44953 };
44954
44955 // private
44956 Roo.BorderLayout.RegionFactory = {
44957     // private
44958     validRegions : ["north","south","east","west","center"],
44959
44960     // private
44961     create : function(target, mgr, config){
44962         target = target.toLowerCase();
44963         if(config.lightweight || config.basic){
44964             return new Roo.BasicLayoutRegion(mgr, config, target);
44965         }
44966         switch(target){
44967             case "north":
44968                 return new Roo.NorthLayoutRegion(mgr, config);
44969             case "south":
44970                 return new Roo.SouthLayoutRegion(mgr, config);
44971             case "east":
44972                 return new Roo.EastLayoutRegion(mgr, config);
44973             case "west":
44974                 return new Roo.WestLayoutRegion(mgr, config);
44975             case "center":
44976                 return new Roo.CenterLayoutRegion(mgr, config);
44977         }
44978         throw 'Layout region "'+target+'" not supported.';
44979     }
44980 };/*
44981  * Based on:
44982  * Ext JS Library 1.1.1
44983  * Copyright(c) 2006-2007, Ext JS, LLC.
44984  *
44985  * Originally Released Under LGPL - original licence link has changed is not relivant.
44986  *
44987  * Fork - LGPL
44988  * <script type="text/javascript">
44989  */
44990  
44991 /**
44992  * @class Roo.BasicLayoutRegion
44993  * @extends Roo.util.Observable
44994  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
44995  * and does not have a titlebar, tabs or any other features. All it does is size and position 
44996  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
44997  */
44998 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
44999     this.mgr = mgr;
45000     this.position  = pos;
45001     this.events = {
45002         /**
45003          * @scope Roo.BasicLayoutRegion
45004          */
45005         
45006         /**
45007          * @event beforeremove
45008          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
45009          * @param {Roo.LayoutRegion} this
45010          * @param {Roo.ContentPanel} panel The panel
45011          * @param {Object} e The cancel event object
45012          */
45013         "beforeremove" : true,
45014         /**
45015          * @event invalidated
45016          * Fires when the layout for this region is changed.
45017          * @param {Roo.LayoutRegion} this
45018          */
45019         "invalidated" : true,
45020         /**
45021          * @event visibilitychange
45022          * Fires when this region is shown or hidden 
45023          * @param {Roo.LayoutRegion} this
45024          * @param {Boolean} visibility true or false
45025          */
45026         "visibilitychange" : true,
45027         /**
45028          * @event paneladded
45029          * Fires when a panel is added. 
45030          * @param {Roo.LayoutRegion} this
45031          * @param {Roo.ContentPanel} panel The panel
45032          */
45033         "paneladded" : true,
45034         /**
45035          * @event panelremoved
45036          * Fires when a panel is removed. 
45037          * @param {Roo.LayoutRegion} this
45038          * @param {Roo.ContentPanel} panel The panel
45039          */
45040         "panelremoved" : true,
45041         /**
45042          * @event collapsed
45043          * Fires when this region is collapsed.
45044          * @param {Roo.LayoutRegion} this
45045          */
45046         "collapsed" : true,
45047         /**
45048          * @event expanded
45049          * Fires when this region is expanded.
45050          * @param {Roo.LayoutRegion} this
45051          */
45052         "expanded" : true,
45053         /**
45054          * @event slideshow
45055          * Fires when this region is slid into view.
45056          * @param {Roo.LayoutRegion} this
45057          */
45058         "slideshow" : true,
45059         /**
45060          * @event slidehide
45061          * Fires when this region slides out of view. 
45062          * @param {Roo.LayoutRegion} this
45063          */
45064         "slidehide" : true,
45065         /**
45066          * @event panelactivated
45067          * Fires when a panel is activated. 
45068          * @param {Roo.LayoutRegion} this
45069          * @param {Roo.ContentPanel} panel The activated panel
45070          */
45071         "panelactivated" : true,
45072         /**
45073          * @event resized
45074          * Fires when the user resizes this region. 
45075          * @param {Roo.LayoutRegion} this
45076          * @param {Number} newSize The new size (width for east/west, height for north/south)
45077          */
45078         "resized" : true
45079     };
45080     /** A collection of panels in this region. @type Roo.util.MixedCollection */
45081     this.panels = new Roo.util.MixedCollection();
45082     this.panels.getKey = this.getPanelId.createDelegate(this);
45083     this.box = null;
45084     this.activePanel = null;
45085     // ensure listeners are added...
45086     
45087     if (config.listeners || config.events) {
45088         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
45089             listeners : config.listeners || {},
45090             events : config.events || {}
45091         });
45092     }
45093     
45094     if(skipConfig !== true){
45095         this.applyConfig(config);
45096     }
45097 };
45098
45099 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
45100     getPanelId : function(p){
45101         return p.getId();
45102     },
45103     
45104     applyConfig : function(config){
45105         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
45106         this.config = config;
45107         
45108     },
45109     
45110     /**
45111      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
45112      * the width, for horizontal (north, south) the height.
45113      * @param {Number} newSize The new width or height
45114      */
45115     resizeTo : function(newSize){
45116         var el = this.el ? this.el :
45117                  (this.activePanel ? this.activePanel.getEl() : null);
45118         if(el){
45119             switch(this.position){
45120                 case "east":
45121                 case "west":
45122                     el.setWidth(newSize);
45123                     this.fireEvent("resized", this, newSize);
45124                 break;
45125                 case "north":
45126                 case "south":
45127                     el.setHeight(newSize);
45128                     this.fireEvent("resized", this, newSize);
45129                 break;                
45130             }
45131         }
45132     },
45133     
45134     getBox : function(){
45135         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
45136     },
45137     
45138     getMargins : function(){
45139         return this.margins;
45140     },
45141     
45142     updateBox : function(box){
45143         this.box = box;
45144         var el = this.activePanel.getEl();
45145         el.dom.style.left = box.x + "px";
45146         el.dom.style.top = box.y + "px";
45147         this.activePanel.setSize(box.width, box.height);
45148     },
45149     
45150     /**
45151      * Returns the container element for this region.
45152      * @return {Roo.Element}
45153      */
45154     getEl : function(){
45155         return this.activePanel;
45156     },
45157     
45158     /**
45159      * Returns true if this region is currently visible.
45160      * @return {Boolean}
45161      */
45162     isVisible : function(){
45163         return this.activePanel ? true : false;
45164     },
45165     
45166     setActivePanel : function(panel){
45167         panel = this.getPanel(panel);
45168         if(this.activePanel && this.activePanel != panel){
45169             this.activePanel.setActiveState(false);
45170             this.activePanel.getEl().setLeftTop(-10000,-10000);
45171         }
45172         this.activePanel = panel;
45173         panel.setActiveState(true);
45174         if(this.box){
45175             panel.setSize(this.box.width, this.box.height);
45176         }
45177         this.fireEvent("panelactivated", this, panel);
45178         this.fireEvent("invalidated");
45179     },
45180     
45181     /**
45182      * Show the specified panel.
45183      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
45184      * @return {Roo.ContentPanel} The shown panel or null
45185      */
45186     showPanel : function(panel){
45187         if(panel = this.getPanel(panel)){
45188             this.setActivePanel(panel);
45189         }
45190         return panel;
45191     },
45192     
45193     /**
45194      * Get the active panel for this region.
45195      * @return {Roo.ContentPanel} The active panel or null
45196      */
45197     getActivePanel : function(){
45198         return this.activePanel;
45199     },
45200     
45201     /**
45202      * Add the passed ContentPanel(s)
45203      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
45204      * @return {Roo.ContentPanel} The panel added (if only one was added)
45205      */
45206     add : function(panel){
45207         if(arguments.length > 1){
45208             for(var i = 0, len = arguments.length; i < len; i++) {
45209                 this.add(arguments[i]);
45210             }
45211             return null;
45212         }
45213         if(this.hasPanel(panel)){
45214             this.showPanel(panel);
45215             return panel;
45216         }
45217         var el = panel.getEl();
45218         if(el.dom.parentNode != this.mgr.el.dom){
45219             this.mgr.el.dom.appendChild(el.dom);
45220         }
45221         if(panel.setRegion){
45222             panel.setRegion(this);
45223         }
45224         this.panels.add(panel);
45225         el.setStyle("position", "absolute");
45226         if(!panel.background){
45227             this.setActivePanel(panel);
45228             if(this.config.initialSize && this.panels.getCount()==1){
45229                 this.resizeTo(this.config.initialSize);
45230             }
45231         }
45232         this.fireEvent("paneladded", this, panel);
45233         return panel;
45234     },
45235     
45236     /**
45237      * Returns true if the panel is in this region.
45238      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
45239      * @return {Boolean}
45240      */
45241     hasPanel : function(panel){
45242         if(typeof panel == "object"){ // must be panel obj
45243             panel = panel.getId();
45244         }
45245         return this.getPanel(panel) ? true : false;
45246     },
45247     
45248     /**
45249      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
45250      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
45251      * @param {Boolean} preservePanel Overrides the config preservePanel option
45252      * @return {Roo.ContentPanel} The panel that was removed
45253      */
45254     remove : function(panel, preservePanel){
45255         panel = this.getPanel(panel);
45256         if(!panel){
45257             return null;
45258         }
45259         var e = {};
45260         this.fireEvent("beforeremove", this, panel, e);
45261         if(e.cancel === true){
45262             return null;
45263         }
45264         var panelId = panel.getId();
45265         this.panels.removeKey(panelId);
45266         return panel;
45267     },
45268     
45269     /**
45270      * Returns the panel specified or null if it's not in this region.
45271      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
45272      * @return {Roo.ContentPanel}
45273      */
45274     getPanel : function(id){
45275         if(typeof id == "object"){ // must be panel obj
45276             return id;
45277         }
45278         return this.panels.get(id);
45279     },
45280     
45281     /**
45282      * Returns this regions position (north/south/east/west/center).
45283      * @return {String} 
45284      */
45285     getPosition: function(){
45286         return this.position;    
45287     }
45288 });/*
45289  * Based on:
45290  * Ext JS Library 1.1.1
45291  * Copyright(c) 2006-2007, Ext JS, LLC.
45292  *
45293  * Originally Released Under LGPL - original licence link has changed is not relivant.
45294  *
45295  * Fork - LGPL
45296  * <script type="text/javascript">
45297  */
45298  
45299 /**
45300  * @class Roo.LayoutRegion
45301  * @extends Roo.BasicLayoutRegion
45302  * This class represents a region in a layout manager.
45303  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
45304  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
45305  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
45306  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
45307  * @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})
45308  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
45309  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
45310  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
45311  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
45312  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
45313  * @cfg {String}    title           The title for the region (overrides panel titles)
45314  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
45315  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
45316  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
45317  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
45318  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
45319  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
45320  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
45321  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
45322  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
45323  * @cfg {Boolean}   showPin         True to show a pin button
45324  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
45325  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
45326  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
45327  * @cfg {Number}    width           For East/West panels
45328  * @cfg {Number}    height          For North/South panels
45329  * @cfg {Boolean}   split           To show the splitter
45330  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
45331  */
45332 Roo.LayoutRegion = function(mgr, config, pos){
45333     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
45334     var dh = Roo.DomHelper;
45335     /** This region's container element 
45336     * @type Roo.Element */
45337     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
45338     /** This region's title element 
45339     * @type Roo.Element */
45340
45341     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
45342         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
45343         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
45344     ]}, true);
45345     this.titleEl.enableDisplayMode();
45346     /** This region's title text element 
45347     * @type HTMLElement */
45348     this.titleTextEl = this.titleEl.dom.firstChild;
45349     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
45350     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
45351     this.closeBtn.enableDisplayMode();
45352     this.closeBtn.on("click", this.closeClicked, this);
45353     this.closeBtn.hide();
45354
45355     this.createBody(config);
45356     this.visible = true;
45357     this.collapsed = false;
45358
45359     if(config.hideWhenEmpty){
45360         this.hide();
45361         this.on("paneladded", this.validateVisibility, this);
45362         this.on("panelremoved", this.validateVisibility, this);
45363     }
45364     this.applyConfig(config);
45365 };
45366
45367 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
45368
45369     createBody : function(){
45370         /** This region's body element 
45371         * @type Roo.Element */
45372         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
45373     },
45374
45375     applyConfig : function(c){
45376         if(c.collapsible && this.position != "center" && !this.collapsedEl){
45377             var dh = Roo.DomHelper;
45378             if(c.titlebar !== false){
45379                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
45380                 this.collapseBtn.on("click", this.collapse, this);
45381                 this.collapseBtn.enableDisplayMode();
45382
45383                 if(c.showPin === true || this.showPin){
45384                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
45385                     this.stickBtn.enableDisplayMode();
45386                     this.stickBtn.on("click", this.expand, this);
45387                     this.stickBtn.hide();
45388                 }
45389             }
45390             /** This region's collapsed element
45391             * @type Roo.Element */
45392             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
45393                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
45394             ]}, true);
45395             if(c.floatable !== false){
45396                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
45397                this.collapsedEl.on("click", this.collapseClick, this);
45398             }
45399
45400             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
45401                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
45402                    id: "message", unselectable: "on", style:{"float":"left"}});
45403                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
45404              }
45405             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
45406             this.expandBtn.on("click", this.expand, this);
45407         }
45408         if(this.collapseBtn){
45409             this.collapseBtn.setVisible(c.collapsible == true);
45410         }
45411         this.cmargins = c.cmargins || this.cmargins ||
45412                          (this.position == "west" || this.position == "east" ?
45413                              {top: 0, left: 2, right:2, bottom: 0} :
45414                              {top: 2, left: 0, right:0, bottom: 2});
45415         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
45416         this.bottomTabs = c.tabPosition != "top";
45417         this.autoScroll = c.autoScroll || false;
45418         if(this.autoScroll){
45419             this.bodyEl.setStyle("overflow", "auto");
45420         }else{
45421             this.bodyEl.setStyle("overflow", "hidden");
45422         }
45423         //if(c.titlebar !== false){
45424             if((!c.titlebar && !c.title) || c.titlebar === false){
45425                 this.titleEl.hide();
45426             }else{
45427                 this.titleEl.show();
45428                 if(c.title){
45429                     this.titleTextEl.innerHTML = c.title;
45430                 }
45431             }
45432         //}
45433         this.duration = c.duration || .30;
45434         this.slideDuration = c.slideDuration || .45;
45435         this.config = c;
45436         if(c.collapsed){
45437             this.collapse(true);
45438         }
45439         if(c.hidden){
45440             this.hide();
45441         }
45442     },
45443     /**
45444      * Returns true if this region is currently visible.
45445      * @return {Boolean}
45446      */
45447     isVisible : function(){
45448         return this.visible;
45449     },
45450
45451     /**
45452      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
45453      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
45454      */
45455     setCollapsedTitle : function(title){
45456         title = title || "&#160;";
45457         if(this.collapsedTitleTextEl){
45458             this.collapsedTitleTextEl.innerHTML = title;
45459         }
45460     },
45461
45462     getBox : function(){
45463         var b;
45464         if(!this.collapsed){
45465             b = this.el.getBox(false, true);
45466         }else{
45467             b = this.collapsedEl.getBox(false, true);
45468         }
45469         return b;
45470     },
45471
45472     getMargins : function(){
45473         return this.collapsed ? this.cmargins : this.margins;
45474     },
45475
45476     highlight : function(){
45477         this.el.addClass("x-layout-panel-dragover");
45478     },
45479
45480     unhighlight : function(){
45481         this.el.removeClass("x-layout-panel-dragover");
45482     },
45483
45484     updateBox : function(box){
45485         this.box = box;
45486         if(!this.collapsed){
45487             this.el.dom.style.left = box.x + "px";
45488             this.el.dom.style.top = box.y + "px";
45489             this.updateBody(box.width, box.height);
45490         }else{
45491             this.collapsedEl.dom.style.left = box.x + "px";
45492             this.collapsedEl.dom.style.top = box.y + "px";
45493             this.collapsedEl.setSize(box.width, box.height);
45494         }
45495         if(this.tabs){
45496             this.tabs.autoSizeTabs();
45497         }
45498     },
45499
45500     updateBody : function(w, h){
45501         if(w !== null){
45502             this.el.setWidth(w);
45503             w -= this.el.getBorderWidth("rl");
45504             if(this.config.adjustments){
45505                 w += this.config.adjustments[0];
45506             }
45507         }
45508         if(h !== null){
45509             this.el.setHeight(h);
45510             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
45511             h -= this.el.getBorderWidth("tb");
45512             if(this.config.adjustments){
45513                 h += this.config.adjustments[1];
45514             }
45515             this.bodyEl.setHeight(h);
45516             if(this.tabs){
45517                 h = this.tabs.syncHeight(h);
45518             }
45519         }
45520         if(this.panelSize){
45521             w = w !== null ? w : this.panelSize.width;
45522             h = h !== null ? h : this.panelSize.height;
45523         }
45524         if(this.activePanel){
45525             var el = this.activePanel.getEl();
45526             w = w !== null ? w : el.getWidth();
45527             h = h !== null ? h : el.getHeight();
45528             this.panelSize = {width: w, height: h};
45529             this.activePanel.setSize(w, h);
45530         }
45531         if(Roo.isIE && this.tabs){
45532             this.tabs.el.repaint();
45533         }
45534     },
45535
45536     /**
45537      * Returns the container element for this region.
45538      * @return {Roo.Element}
45539      */
45540     getEl : function(){
45541         return this.el;
45542     },
45543
45544     /**
45545      * Hides this region.
45546      */
45547     hide : function(){
45548         if(!this.collapsed){
45549             this.el.dom.style.left = "-2000px";
45550             this.el.hide();
45551         }else{
45552             this.collapsedEl.dom.style.left = "-2000px";
45553             this.collapsedEl.hide();
45554         }
45555         this.visible = false;
45556         this.fireEvent("visibilitychange", this, false);
45557     },
45558
45559     /**
45560      * Shows this region if it was previously hidden.
45561      */
45562     show : function(){
45563         if(!this.collapsed){
45564             this.el.show();
45565         }else{
45566             this.collapsedEl.show();
45567         }
45568         this.visible = true;
45569         this.fireEvent("visibilitychange", this, true);
45570     },
45571
45572     closeClicked : function(){
45573         if(this.activePanel){
45574             this.remove(this.activePanel);
45575         }
45576     },
45577
45578     collapseClick : function(e){
45579         if(this.isSlid){
45580            e.stopPropagation();
45581            this.slideIn();
45582         }else{
45583            e.stopPropagation();
45584            this.slideOut();
45585         }
45586     },
45587
45588     /**
45589      * Collapses this region.
45590      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
45591      */
45592     collapse : function(skipAnim){
45593         if(this.collapsed) return;
45594         this.collapsed = true;
45595         if(this.split){
45596             this.split.el.hide();
45597         }
45598         if(this.config.animate && skipAnim !== true){
45599             this.fireEvent("invalidated", this);
45600             this.animateCollapse();
45601         }else{
45602             this.el.setLocation(-20000,-20000);
45603             this.el.hide();
45604             this.collapsedEl.show();
45605             this.fireEvent("collapsed", this);
45606             this.fireEvent("invalidated", this);
45607         }
45608     },
45609
45610     animateCollapse : function(){
45611         // overridden
45612     },
45613
45614     /**
45615      * Expands this region if it was previously collapsed.
45616      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
45617      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
45618      */
45619     expand : function(e, skipAnim){
45620         if(e) e.stopPropagation();
45621         if(!this.collapsed || this.el.hasActiveFx()) return;
45622         if(this.isSlid){
45623             this.afterSlideIn();
45624             skipAnim = true;
45625         }
45626         this.collapsed = false;
45627         if(this.config.animate && skipAnim !== true){
45628             this.animateExpand();
45629         }else{
45630             this.el.show();
45631             if(this.split){
45632                 this.split.el.show();
45633             }
45634             this.collapsedEl.setLocation(-2000,-2000);
45635             this.collapsedEl.hide();
45636             this.fireEvent("invalidated", this);
45637             this.fireEvent("expanded", this);
45638         }
45639     },
45640
45641     animateExpand : function(){
45642         // overridden
45643     },
45644
45645     initTabs : function()
45646     {
45647         this.bodyEl.setStyle("overflow", "hidden");
45648         var ts = new Roo.TabPanel(
45649                 this.bodyEl.dom,
45650                 {
45651                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
45652                     disableTooltips: this.config.disableTabTips,
45653                     toolbar : this.config.toolbar
45654                 }
45655         );
45656         if(this.config.hideTabs){
45657             ts.stripWrap.setDisplayed(false);
45658         }
45659         this.tabs = ts;
45660         ts.resizeTabs = this.config.resizeTabs === true;
45661         ts.minTabWidth = this.config.minTabWidth || 40;
45662         ts.maxTabWidth = this.config.maxTabWidth || 250;
45663         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
45664         ts.monitorResize = false;
45665         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
45666         ts.bodyEl.addClass('x-layout-tabs-body');
45667         this.panels.each(this.initPanelAsTab, this);
45668     },
45669
45670     initPanelAsTab : function(panel){
45671         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
45672                     this.config.closeOnTab && panel.isClosable());
45673         if(panel.tabTip !== undefined){
45674             ti.setTooltip(panel.tabTip);
45675         }
45676         ti.on("activate", function(){
45677               this.setActivePanel(panel);
45678         }, this);
45679         if(this.config.closeOnTab){
45680             ti.on("beforeclose", function(t, e){
45681                 e.cancel = true;
45682                 this.remove(panel);
45683             }, this);
45684         }
45685         return ti;
45686     },
45687
45688     updatePanelTitle : function(panel, title){
45689         if(this.activePanel == panel){
45690             this.updateTitle(title);
45691         }
45692         if(this.tabs){
45693             var ti = this.tabs.getTab(panel.getEl().id);
45694             ti.setText(title);
45695             if(panel.tabTip !== undefined){
45696                 ti.setTooltip(panel.tabTip);
45697             }
45698         }
45699     },
45700
45701     updateTitle : function(title){
45702         if(this.titleTextEl && !this.config.title){
45703             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
45704         }
45705     },
45706
45707     setActivePanel : function(panel){
45708         panel = this.getPanel(panel);
45709         if(this.activePanel && this.activePanel != panel){
45710             this.activePanel.setActiveState(false);
45711         }
45712         this.activePanel = panel;
45713         panel.setActiveState(true);
45714         if(this.panelSize){
45715             panel.setSize(this.panelSize.width, this.panelSize.height);
45716         }
45717         if(this.closeBtn){
45718             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
45719         }
45720         this.updateTitle(panel.getTitle());
45721         if(this.tabs){
45722             this.fireEvent("invalidated", this);
45723         }
45724         this.fireEvent("panelactivated", this, panel);
45725     },
45726
45727     /**
45728      * Shows the specified panel.
45729      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
45730      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
45731      */
45732     showPanel : function(panel){
45733         if(panel = this.getPanel(panel)){
45734             if(this.tabs){
45735                 var tab = this.tabs.getTab(panel.getEl().id);
45736                 if(tab.isHidden()){
45737                     this.tabs.unhideTab(tab.id);
45738                 }
45739                 tab.activate();
45740             }else{
45741                 this.setActivePanel(panel);
45742             }
45743         }
45744         return panel;
45745     },
45746
45747     /**
45748      * Get the active panel for this region.
45749      * @return {Roo.ContentPanel} The active panel or null
45750      */
45751     getActivePanel : function(){
45752         return this.activePanel;
45753     },
45754
45755     validateVisibility : function(){
45756         if(this.panels.getCount() < 1){
45757             this.updateTitle("&#160;");
45758             this.closeBtn.hide();
45759             this.hide();
45760         }else{
45761             if(!this.isVisible()){
45762                 this.show();
45763             }
45764         }
45765     },
45766
45767     /**
45768      * Adds the passed ContentPanel(s) to this region.
45769      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
45770      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
45771      */
45772     add : function(panel){
45773         if(arguments.length > 1){
45774             for(var i = 0, len = arguments.length; i < len; i++) {
45775                 this.add(arguments[i]);
45776             }
45777             return null;
45778         }
45779         if(this.hasPanel(panel)){
45780             this.showPanel(panel);
45781             return panel;
45782         }
45783         panel.setRegion(this);
45784         this.panels.add(panel);
45785         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
45786             this.bodyEl.dom.appendChild(panel.getEl().dom);
45787             if(panel.background !== true){
45788                 this.setActivePanel(panel);
45789             }
45790             this.fireEvent("paneladded", this, panel);
45791             return panel;
45792         }
45793         if(!this.tabs){
45794             this.initTabs();
45795         }else{
45796             this.initPanelAsTab(panel);
45797         }
45798         if(panel.background !== true){
45799             this.tabs.activate(panel.getEl().id);
45800         }
45801         this.fireEvent("paneladded", this, panel);
45802         return panel;
45803     },
45804
45805     /**
45806      * Hides the tab for the specified panel.
45807      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
45808      */
45809     hidePanel : function(panel){
45810         if(this.tabs && (panel = this.getPanel(panel))){
45811             this.tabs.hideTab(panel.getEl().id);
45812         }
45813     },
45814
45815     /**
45816      * Unhides the tab for a previously hidden panel.
45817      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
45818      */
45819     unhidePanel : function(panel){
45820         if(this.tabs && (panel = this.getPanel(panel))){
45821             this.tabs.unhideTab(panel.getEl().id);
45822         }
45823     },
45824
45825     clearPanels : function(){
45826         while(this.panels.getCount() > 0){
45827              this.remove(this.panels.first());
45828         }
45829     },
45830
45831     /**
45832      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
45833      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
45834      * @param {Boolean} preservePanel Overrides the config preservePanel option
45835      * @return {Roo.ContentPanel} The panel that was removed
45836      */
45837     remove : function(panel, preservePanel){
45838         panel = this.getPanel(panel);
45839         if(!panel){
45840             return null;
45841         }
45842         var e = {};
45843         this.fireEvent("beforeremove", this, panel, e);
45844         if(e.cancel === true){
45845             return null;
45846         }
45847         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
45848         var panelId = panel.getId();
45849         this.panels.removeKey(panelId);
45850         if(preservePanel){
45851             document.body.appendChild(panel.getEl().dom);
45852         }
45853         if(this.tabs){
45854             this.tabs.removeTab(panel.getEl().id);
45855         }else if (!preservePanel){
45856             this.bodyEl.dom.removeChild(panel.getEl().dom);
45857         }
45858         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
45859             var p = this.panels.first();
45860             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
45861             tempEl.appendChild(p.getEl().dom);
45862             this.bodyEl.update("");
45863             this.bodyEl.dom.appendChild(p.getEl().dom);
45864             tempEl = null;
45865             this.updateTitle(p.getTitle());
45866             this.tabs = null;
45867             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
45868             this.setActivePanel(p);
45869         }
45870         panel.setRegion(null);
45871         if(this.activePanel == panel){
45872             this.activePanel = null;
45873         }
45874         if(this.config.autoDestroy !== false && preservePanel !== true){
45875             try{panel.destroy();}catch(e){}
45876         }
45877         this.fireEvent("panelremoved", this, panel);
45878         return panel;
45879     },
45880
45881     /**
45882      * Returns the TabPanel component used by this region
45883      * @return {Roo.TabPanel}
45884      */
45885     getTabs : function(){
45886         return this.tabs;
45887     },
45888
45889     createTool : function(parentEl, className){
45890         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
45891             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
45892         btn.addClassOnOver("x-layout-tools-button-over");
45893         return btn;
45894     }
45895 });/*
45896  * Based on:
45897  * Ext JS Library 1.1.1
45898  * Copyright(c) 2006-2007, Ext JS, LLC.
45899  *
45900  * Originally Released Under LGPL - original licence link has changed is not relivant.
45901  *
45902  * Fork - LGPL
45903  * <script type="text/javascript">
45904  */
45905  
45906
45907
45908 /**
45909  * @class Roo.SplitLayoutRegion
45910  * @extends Roo.LayoutRegion
45911  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
45912  */
45913 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
45914     this.cursor = cursor;
45915     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
45916 };
45917
45918 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
45919     splitTip : "Drag to resize.",
45920     collapsibleSplitTip : "Drag to resize. Double click to hide.",
45921     useSplitTips : false,
45922
45923     applyConfig : function(config){
45924         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
45925         if(config.split){
45926             if(!this.split){
45927                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
45928                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
45929                 /** The SplitBar for this region 
45930                 * @type Roo.SplitBar */
45931                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
45932                 this.split.on("moved", this.onSplitMove, this);
45933                 this.split.useShim = config.useShim === true;
45934                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
45935                 if(this.useSplitTips){
45936                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
45937                 }
45938                 if(config.collapsible){
45939                     this.split.el.on("dblclick", this.collapse,  this);
45940                 }
45941             }
45942             if(typeof config.minSize != "undefined"){
45943                 this.split.minSize = config.minSize;
45944             }
45945             if(typeof config.maxSize != "undefined"){
45946                 this.split.maxSize = config.maxSize;
45947             }
45948             if(config.hideWhenEmpty || config.hidden || config.collapsed){
45949                 this.hideSplitter();
45950             }
45951         }
45952     },
45953
45954     getHMaxSize : function(){
45955          var cmax = this.config.maxSize || 10000;
45956          var center = this.mgr.getRegion("center");
45957          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
45958     },
45959
45960     getVMaxSize : function(){
45961          var cmax = this.config.maxSize || 10000;
45962          var center = this.mgr.getRegion("center");
45963          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
45964     },
45965
45966     onSplitMove : function(split, newSize){
45967         this.fireEvent("resized", this, newSize);
45968     },
45969     
45970     /** 
45971      * Returns the {@link Roo.SplitBar} for this region.
45972      * @return {Roo.SplitBar}
45973      */
45974     getSplitBar : function(){
45975         return this.split;
45976     },
45977     
45978     hide : function(){
45979         this.hideSplitter();
45980         Roo.SplitLayoutRegion.superclass.hide.call(this);
45981     },
45982
45983     hideSplitter : function(){
45984         if(this.split){
45985             this.split.el.setLocation(-2000,-2000);
45986             this.split.el.hide();
45987         }
45988     },
45989
45990     show : function(){
45991         if(this.split){
45992             this.split.el.show();
45993         }
45994         Roo.SplitLayoutRegion.superclass.show.call(this);
45995     },
45996     
45997     beforeSlide: function(){
45998         if(Roo.isGecko){// firefox overflow auto bug workaround
45999             this.bodyEl.clip();
46000             if(this.tabs) this.tabs.bodyEl.clip();
46001             if(this.activePanel){
46002                 this.activePanel.getEl().clip();
46003                 
46004                 if(this.activePanel.beforeSlide){
46005                     this.activePanel.beforeSlide();
46006                 }
46007             }
46008         }
46009     },
46010     
46011     afterSlide : function(){
46012         if(Roo.isGecko){// firefox overflow auto bug workaround
46013             this.bodyEl.unclip();
46014             if(this.tabs) this.tabs.bodyEl.unclip();
46015             if(this.activePanel){
46016                 this.activePanel.getEl().unclip();
46017                 if(this.activePanel.afterSlide){
46018                     this.activePanel.afterSlide();
46019                 }
46020             }
46021         }
46022     },
46023
46024     initAutoHide : function(){
46025         if(this.autoHide !== false){
46026             if(!this.autoHideHd){
46027                 var st = new Roo.util.DelayedTask(this.slideIn, this);
46028                 this.autoHideHd = {
46029                     "mouseout": function(e){
46030                         if(!e.within(this.el, true)){
46031                             st.delay(500);
46032                         }
46033                     },
46034                     "mouseover" : function(e){
46035                         st.cancel();
46036                     },
46037                     scope : this
46038                 };
46039             }
46040             this.el.on(this.autoHideHd);
46041         }
46042     },
46043
46044     clearAutoHide : function(){
46045         if(this.autoHide !== false){
46046             this.el.un("mouseout", this.autoHideHd.mouseout);
46047             this.el.un("mouseover", this.autoHideHd.mouseover);
46048         }
46049     },
46050
46051     clearMonitor : function(){
46052         Roo.get(document).un("click", this.slideInIf, this);
46053     },
46054
46055     // these names are backwards but not changed for compat
46056     slideOut : function(){
46057         if(this.isSlid || this.el.hasActiveFx()){
46058             return;
46059         }
46060         this.isSlid = true;
46061         if(this.collapseBtn){
46062             this.collapseBtn.hide();
46063         }
46064         this.closeBtnState = this.closeBtn.getStyle('display');
46065         this.closeBtn.hide();
46066         if(this.stickBtn){
46067             this.stickBtn.show();
46068         }
46069         this.el.show();
46070         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
46071         this.beforeSlide();
46072         this.el.setStyle("z-index", 10001);
46073         this.el.slideIn(this.getSlideAnchor(), {
46074             callback: function(){
46075                 this.afterSlide();
46076                 this.initAutoHide();
46077                 Roo.get(document).on("click", this.slideInIf, this);
46078                 this.fireEvent("slideshow", this);
46079             },
46080             scope: this,
46081             block: true
46082         });
46083     },
46084
46085     afterSlideIn : function(){
46086         this.clearAutoHide();
46087         this.isSlid = false;
46088         this.clearMonitor();
46089         this.el.setStyle("z-index", "");
46090         if(this.collapseBtn){
46091             this.collapseBtn.show();
46092         }
46093         this.closeBtn.setStyle('display', this.closeBtnState);
46094         if(this.stickBtn){
46095             this.stickBtn.hide();
46096         }
46097         this.fireEvent("slidehide", this);
46098     },
46099
46100     slideIn : function(cb){
46101         if(!this.isSlid || this.el.hasActiveFx()){
46102             Roo.callback(cb);
46103             return;
46104         }
46105         this.isSlid = false;
46106         this.beforeSlide();
46107         this.el.slideOut(this.getSlideAnchor(), {
46108             callback: function(){
46109                 this.el.setLeftTop(-10000, -10000);
46110                 this.afterSlide();
46111                 this.afterSlideIn();
46112                 Roo.callback(cb);
46113             },
46114             scope: this,
46115             block: true
46116         });
46117     },
46118     
46119     slideInIf : function(e){
46120         if(!e.within(this.el)){
46121             this.slideIn();
46122         }
46123     },
46124
46125     animateCollapse : function(){
46126         this.beforeSlide();
46127         this.el.setStyle("z-index", 20000);
46128         var anchor = this.getSlideAnchor();
46129         this.el.slideOut(anchor, {
46130             callback : function(){
46131                 this.el.setStyle("z-index", "");
46132                 this.collapsedEl.slideIn(anchor, {duration:.3});
46133                 this.afterSlide();
46134                 this.el.setLocation(-10000,-10000);
46135                 this.el.hide();
46136                 this.fireEvent("collapsed", this);
46137             },
46138             scope: this,
46139             block: true
46140         });
46141     },
46142
46143     animateExpand : function(){
46144         this.beforeSlide();
46145         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
46146         this.el.setStyle("z-index", 20000);
46147         this.collapsedEl.hide({
46148             duration:.1
46149         });
46150         this.el.slideIn(this.getSlideAnchor(), {
46151             callback : function(){
46152                 this.el.setStyle("z-index", "");
46153                 this.afterSlide();
46154                 if(this.split){
46155                     this.split.el.show();
46156                 }
46157                 this.fireEvent("invalidated", this);
46158                 this.fireEvent("expanded", this);
46159             },
46160             scope: this,
46161             block: true
46162         });
46163     },
46164
46165     anchors : {
46166         "west" : "left",
46167         "east" : "right",
46168         "north" : "top",
46169         "south" : "bottom"
46170     },
46171
46172     sanchors : {
46173         "west" : "l",
46174         "east" : "r",
46175         "north" : "t",
46176         "south" : "b"
46177     },
46178
46179     canchors : {
46180         "west" : "tl-tr",
46181         "east" : "tr-tl",
46182         "north" : "tl-bl",
46183         "south" : "bl-tl"
46184     },
46185
46186     getAnchor : function(){
46187         return this.anchors[this.position];
46188     },
46189
46190     getCollapseAnchor : function(){
46191         return this.canchors[this.position];
46192     },
46193
46194     getSlideAnchor : function(){
46195         return this.sanchors[this.position];
46196     },
46197
46198     getAlignAdj : function(){
46199         var cm = this.cmargins;
46200         switch(this.position){
46201             case "west":
46202                 return [0, 0];
46203             break;
46204             case "east":
46205                 return [0, 0];
46206             break;
46207             case "north":
46208                 return [0, 0];
46209             break;
46210             case "south":
46211                 return [0, 0];
46212             break;
46213         }
46214     },
46215
46216     getExpandAdj : function(){
46217         var c = this.collapsedEl, cm = this.cmargins;
46218         switch(this.position){
46219             case "west":
46220                 return [-(cm.right+c.getWidth()+cm.left), 0];
46221             break;
46222             case "east":
46223                 return [cm.right+c.getWidth()+cm.left, 0];
46224             break;
46225             case "north":
46226                 return [0, -(cm.top+cm.bottom+c.getHeight())];
46227             break;
46228             case "south":
46229                 return [0, cm.top+cm.bottom+c.getHeight()];
46230             break;
46231         }
46232     }
46233 });/*
46234  * Based on:
46235  * Ext JS Library 1.1.1
46236  * Copyright(c) 2006-2007, Ext JS, LLC.
46237  *
46238  * Originally Released Under LGPL - original licence link has changed is not relivant.
46239  *
46240  * Fork - LGPL
46241  * <script type="text/javascript">
46242  */
46243 /*
46244  * These classes are private internal classes
46245  */
46246 Roo.CenterLayoutRegion = function(mgr, config){
46247     Roo.LayoutRegion.call(this, mgr, config, "center");
46248     this.visible = true;
46249     this.minWidth = config.minWidth || 20;
46250     this.minHeight = config.minHeight || 20;
46251 };
46252
46253 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
46254     hide : function(){
46255         // center panel can't be hidden
46256     },
46257     
46258     show : function(){
46259         // center panel can't be hidden
46260     },
46261     
46262     getMinWidth: function(){
46263         return this.minWidth;
46264     },
46265     
46266     getMinHeight: function(){
46267         return this.minHeight;
46268     }
46269 });
46270
46271
46272 Roo.NorthLayoutRegion = function(mgr, config){
46273     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
46274     if(this.split){
46275         this.split.placement = Roo.SplitBar.TOP;
46276         this.split.orientation = Roo.SplitBar.VERTICAL;
46277         this.split.el.addClass("x-layout-split-v");
46278     }
46279     var size = config.initialSize || config.height;
46280     if(typeof size != "undefined"){
46281         this.el.setHeight(size);
46282     }
46283 };
46284 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
46285     orientation: Roo.SplitBar.VERTICAL,
46286     getBox : function(){
46287         if(this.collapsed){
46288             return this.collapsedEl.getBox();
46289         }
46290         var box = this.el.getBox();
46291         if(this.split){
46292             box.height += this.split.el.getHeight();
46293         }
46294         return box;
46295     },
46296     
46297     updateBox : function(box){
46298         if(this.split && !this.collapsed){
46299             box.height -= this.split.el.getHeight();
46300             this.split.el.setLeft(box.x);
46301             this.split.el.setTop(box.y+box.height);
46302             this.split.el.setWidth(box.width);
46303         }
46304         if(this.collapsed){
46305             this.updateBody(box.width, null);
46306         }
46307         Roo.LayoutRegion.prototype.updateBox.call(this, box);
46308     }
46309 });
46310
46311 Roo.SouthLayoutRegion = function(mgr, config){
46312     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
46313     if(this.split){
46314         this.split.placement = Roo.SplitBar.BOTTOM;
46315         this.split.orientation = Roo.SplitBar.VERTICAL;
46316         this.split.el.addClass("x-layout-split-v");
46317     }
46318     var size = config.initialSize || config.height;
46319     if(typeof size != "undefined"){
46320         this.el.setHeight(size);
46321     }
46322 };
46323 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
46324     orientation: Roo.SplitBar.VERTICAL,
46325     getBox : function(){
46326         if(this.collapsed){
46327             return this.collapsedEl.getBox();
46328         }
46329         var box = this.el.getBox();
46330         if(this.split){
46331             var sh = this.split.el.getHeight();
46332             box.height += sh;
46333             box.y -= sh;
46334         }
46335         return box;
46336     },
46337     
46338     updateBox : function(box){
46339         if(this.split && !this.collapsed){
46340             var sh = this.split.el.getHeight();
46341             box.height -= sh;
46342             box.y += sh;
46343             this.split.el.setLeft(box.x);
46344             this.split.el.setTop(box.y-sh);
46345             this.split.el.setWidth(box.width);
46346         }
46347         if(this.collapsed){
46348             this.updateBody(box.width, null);
46349         }
46350         Roo.LayoutRegion.prototype.updateBox.call(this, box);
46351     }
46352 });
46353
46354 Roo.EastLayoutRegion = function(mgr, config){
46355     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
46356     if(this.split){
46357         this.split.placement = Roo.SplitBar.RIGHT;
46358         this.split.orientation = Roo.SplitBar.HORIZONTAL;
46359         this.split.el.addClass("x-layout-split-h");
46360     }
46361     var size = config.initialSize || config.width;
46362     if(typeof size != "undefined"){
46363         this.el.setWidth(size);
46364     }
46365 };
46366 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
46367     orientation: Roo.SplitBar.HORIZONTAL,
46368     getBox : function(){
46369         if(this.collapsed){
46370             return this.collapsedEl.getBox();
46371         }
46372         var box = this.el.getBox();
46373         if(this.split){
46374             var sw = this.split.el.getWidth();
46375             box.width += sw;
46376             box.x -= sw;
46377         }
46378         return box;
46379     },
46380
46381     updateBox : function(box){
46382         if(this.split && !this.collapsed){
46383             var sw = this.split.el.getWidth();
46384             box.width -= sw;
46385             this.split.el.setLeft(box.x);
46386             this.split.el.setTop(box.y);
46387             this.split.el.setHeight(box.height);
46388             box.x += sw;
46389         }
46390         if(this.collapsed){
46391             this.updateBody(null, box.height);
46392         }
46393         Roo.LayoutRegion.prototype.updateBox.call(this, box);
46394     }
46395 });
46396
46397 Roo.WestLayoutRegion = function(mgr, config){
46398     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
46399     if(this.split){
46400         this.split.placement = Roo.SplitBar.LEFT;
46401         this.split.orientation = Roo.SplitBar.HORIZONTAL;
46402         this.split.el.addClass("x-layout-split-h");
46403     }
46404     var size = config.initialSize || config.width;
46405     if(typeof size != "undefined"){
46406         this.el.setWidth(size);
46407     }
46408 };
46409 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
46410     orientation: Roo.SplitBar.HORIZONTAL,
46411     getBox : function(){
46412         if(this.collapsed){
46413             return this.collapsedEl.getBox();
46414         }
46415         var box = this.el.getBox();
46416         if(this.split){
46417             box.width += this.split.el.getWidth();
46418         }
46419         return box;
46420     },
46421     
46422     updateBox : function(box){
46423         if(this.split && !this.collapsed){
46424             var sw = this.split.el.getWidth();
46425             box.width -= sw;
46426             this.split.el.setLeft(box.x+box.width);
46427             this.split.el.setTop(box.y);
46428             this.split.el.setHeight(box.height);
46429         }
46430         if(this.collapsed){
46431             this.updateBody(null, box.height);
46432         }
46433         Roo.LayoutRegion.prototype.updateBox.call(this, box);
46434     }
46435 });
46436 /*
46437  * Based on:
46438  * Ext JS Library 1.1.1
46439  * Copyright(c) 2006-2007, Ext JS, LLC.
46440  *
46441  * Originally Released Under LGPL - original licence link has changed is not relivant.
46442  *
46443  * Fork - LGPL
46444  * <script type="text/javascript">
46445  */
46446  
46447  
46448 /*
46449  * Private internal class for reading and applying state
46450  */
46451 Roo.LayoutStateManager = function(layout){
46452      // default empty state
46453      this.state = {
46454         north: {},
46455         south: {},
46456         east: {},
46457         west: {}       
46458     };
46459 };
46460
46461 Roo.LayoutStateManager.prototype = {
46462     init : function(layout, provider){
46463         this.provider = provider;
46464         var state = provider.get(layout.id+"-layout-state");
46465         if(state){
46466             var wasUpdating = layout.isUpdating();
46467             if(!wasUpdating){
46468                 layout.beginUpdate();
46469             }
46470             for(var key in state){
46471                 if(typeof state[key] != "function"){
46472                     var rstate = state[key];
46473                     var r = layout.getRegion(key);
46474                     if(r && rstate){
46475                         if(rstate.size){
46476                             r.resizeTo(rstate.size);
46477                         }
46478                         if(rstate.collapsed == true){
46479                             r.collapse(true);
46480                         }else{
46481                             r.expand(null, true);
46482                         }
46483                     }
46484                 }
46485             }
46486             if(!wasUpdating){
46487                 layout.endUpdate();
46488             }
46489             this.state = state; 
46490         }
46491         this.layout = layout;
46492         layout.on("regionresized", this.onRegionResized, this);
46493         layout.on("regioncollapsed", this.onRegionCollapsed, this);
46494         layout.on("regionexpanded", this.onRegionExpanded, this);
46495     },
46496     
46497     storeState : function(){
46498         this.provider.set(this.layout.id+"-layout-state", this.state);
46499     },
46500     
46501     onRegionResized : function(region, newSize){
46502         this.state[region.getPosition()].size = newSize;
46503         this.storeState();
46504     },
46505     
46506     onRegionCollapsed : function(region){
46507         this.state[region.getPosition()].collapsed = true;
46508         this.storeState();
46509     },
46510     
46511     onRegionExpanded : function(region){
46512         this.state[region.getPosition()].collapsed = false;
46513         this.storeState();
46514     }
46515 };/*
46516  * Based on:
46517  * Ext JS Library 1.1.1
46518  * Copyright(c) 2006-2007, Ext JS, LLC.
46519  *
46520  * Originally Released Under LGPL - original licence link has changed is not relivant.
46521  *
46522  * Fork - LGPL
46523  * <script type="text/javascript">
46524  */
46525 /**
46526  * @class Roo.ContentPanel
46527  * @extends Roo.util.Observable
46528  * A basic ContentPanel element.
46529  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
46530  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
46531  * @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
46532  * @cfg {Boolean}   closable      True if the panel can be closed/removed
46533  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
46534  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
46535  * @cfg {Toolbar}   toolbar       A toolbar for this panel
46536  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
46537  * @cfg {String} title          The title for this panel
46538  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
46539  * @cfg {String} url            Calls {@link #setUrl} with this value
46540  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
46541  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
46542  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
46543  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
46544
46545  * @constructor
46546  * Create a new ContentPanel.
46547  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
46548  * @param {String/Object} config A string to set only the title or a config object
46549  * @param {String} content (optional) Set the HTML content for this panel
46550  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
46551  */
46552 Roo.ContentPanel = function(el, config, content){
46553     
46554      
46555     /*
46556     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
46557         config = el;
46558         el = Roo.id();
46559     }
46560     if (config && config.parentLayout) { 
46561         el = config.parentLayout.el.createChild(); 
46562     }
46563     */
46564     if(el.autoCreate){ // xtype is available if this is called from factory
46565         config = el;
46566         el = Roo.id();
46567     }
46568     this.el = Roo.get(el);
46569     if(!this.el && config && config.autoCreate){
46570         if(typeof config.autoCreate == "object"){
46571             if(!config.autoCreate.id){
46572                 config.autoCreate.id = config.id||el;
46573             }
46574             this.el = Roo.DomHelper.append(document.body,
46575                         config.autoCreate, true);
46576         }else{
46577             this.el = Roo.DomHelper.append(document.body,
46578                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
46579         }
46580     }
46581     this.closable = false;
46582     this.loaded = false;
46583     this.active = false;
46584     if(typeof config == "string"){
46585         this.title = config;
46586     }else{
46587         Roo.apply(this, config);
46588     }
46589     
46590     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
46591         this.wrapEl = this.el.wrap();
46592         this.toolbar.container = this.el.insertSibling(false, 'before');
46593         this.toolbar = new Roo.Toolbar(this.toolbar);
46594     }
46595     
46596     
46597     
46598     if(this.resizeEl){
46599         this.resizeEl = Roo.get(this.resizeEl, true);
46600     }else{
46601         this.resizeEl = this.el;
46602     }
46603     this.addEvents({
46604         /**
46605          * @event activate
46606          * Fires when this panel is activated. 
46607          * @param {Roo.ContentPanel} this
46608          */
46609         "activate" : true,
46610         /**
46611          * @event deactivate
46612          * Fires when this panel is activated. 
46613          * @param {Roo.ContentPanel} this
46614          */
46615         "deactivate" : true,
46616
46617         /**
46618          * @event resize
46619          * Fires when this panel is resized if fitToFrame is true.
46620          * @param {Roo.ContentPanel} this
46621          * @param {Number} width The width after any component adjustments
46622          * @param {Number} height The height after any component adjustments
46623          */
46624         "resize" : true,
46625         
46626          /**
46627          * @event render
46628          * Fires when this tab is created
46629          * @param {Roo.ContentPanel} this
46630          */
46631         "render" : true
46632         
46633         
46634         
46635     });
46636     if(this.autoScroll){
46637         this.resizeEl.setStyle("overflow", "auto");
46638     } else {
46639         // fix randome scrolling
46640         this.el.on('scroll', function() {
46641             Roo.log('fix random scolling');
46642             this.scrollTo('top',0); 
46643         });
46644     }
46645     content = content || this.content;
46646     if(content){
46647         this.setContent(content);
46648     }
46649     if(config && config.url){
46650         this.setUrl(this.url, this.params, this.loadOnce);
46651     }
46652     
46653     
46654     
46655     Roo.ContentPanel.superclass.constructor.call(this);
46656     
46657     this.fireEvent('render', this);
46658 };
46659
46660 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
46661     tabTip:'',
46662     setRegion : function(region){
46663         this.region = region;
46664         if(region){
46665            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
46666         }else{
46667            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
46668         } 
46669     },
46670     
46671     /**
46672      * Returns the toolbar for this Panel if one was configured. 
46673      * @return {Roo.Toolbar} 
46674      */
46675     getToolbar : function(){
46676         return this.toolbar;
46677     },
46678     
46679     setActiveState : function(active){
46680         this.active = active;
46681         if(!active){
46682             this.fireEvent("deactivate", this);
46683         }else{
46684             this.fireEvent("activate", this);
46685         }
46686     },
46687     /**
46688      * Updates this panel's element
46689      * @param {String} content The new content
46690      * @param {Boolean} loadScripts (optional) true to look for and process scripts
46691     */
46692     setContent : function(content, loadScripts){
46693         this.el.update(content, loadScripts);
46694     },
46695
46696     ignoreResize : function(w, h){
46697         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
46698             return true;
46699         }else{
46700             this.lastSize = {width: w, height: h};
46701             return false;
46702         }
46703     },
46704     /**
46705      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
46706      * @return {Roo.UpdateManager} The UpdateManager
46707      */
46708     getUpdateManager : function(){
46709         return this.el.getUpdateManager();
46710     },
46711      /**
46712      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
46713      * @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:
46714 <pre><code>
46715 panel.load({
46716     url: "your-url.php",
46717     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
46718     callback: yourFunction,
46719     scope: yourObject, //(optional scope)
46720     discardUrl: false,
46721     nocache: false,
46722     text: "Loading...",
46723     timeout: 30,
46724     scripts: false
46725 });
46726 </code></pre>
46727      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
46728      * 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.
46729      * @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}
46730      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
46731      * @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.
46732      * @return {Roo.ContentPanel} this
46733      */
46734     load : function(){
46735         var um = this.el.getUpdateManager();
46736         um.update.apply(um, arguments);
46737         return this;
46738     },
46739
46740
46741     /**
46742      * 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.
46743      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
46744      * @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)
46745      * @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)
46746      * @return {Roo.UpdateManager} The UpdateManager
46747      */
46748     setUrl : function(url, params, loadOnce){
46749         if(this.refreshDelegate){
46750             this.removeListener("activate", this.refreshDelegate);
46751         }
46752         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
46753         this.on("activate", this.refreshDelegate);
46754         return this.el.getUpdateManager();
46755     },
46756     
46757     _handleRefresh : function(url, params, loadOnce){
46758         if(!loadOnce || !this.loaded){
46759             var updater = this.el.getUpdateManager();
46760             updater.update(url, params, this._setLoaded.createDelegate(this));
46761         }
46762     },
46763     
46764     _setLoaded : function(){
46765         this.loaded = true;
46766     }, 
46767     
46768     /**
46769      * Returns this panel's id
46770      * @return {String} 
46771      */
46772     getId : function(){
46773         return this.el.id;
46774     },
46775     
46776     /** 
46777      * Returns this panel's element - used by regiosn to add.
46778      * @return {Roo.Element} 
46779      */
46780     getEl : function(){
46781         return this.wrapEl || this.el;
46782     },
46783     
46784     adjustForComponents : function(width, height){
46785         if(this.resizeEl != this.el){
46786             width -= this.el.getFrameWidth('lr');
46787             height -= this.el.getFrameWidth('tb');
46788         }
46789         if(this.toolbar){
46790             var te = this.toolbar.getEl();
46791             height -= te.getHeight();
46792             te.setWidth(width);
46793         }
46794         if(this.adjustments){
46795             width += this.adjustments[0];
46796             height += this.adjustments[1];
46797         }
46798         return {"width": width, "height": height};
46799     },
46800     
46801     setSize : function(width, height){
46802         if(this.fitToFrame && !this.ignoreResize(width, height)){
46803             if(this.fitContainer && this.resizeEl != this.el){
46804                 this.el.setSize(width, height);
46805             }
46806             var size = this.adjustForComponents(width, height);
46807             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
46808             this.fireEvent('resize', this, size.width, size.height);
46809         }
46810     },
46811     
46812     /**
46813      * Returns this panel's title
46814      * @return {String} 
46815      */
46816     getTitle : function(){
46817         return this.title;
46818     },
46819     
46820     /**
46821      * Set this panel's title
46822      * @param {String} title
46823      */
46824     setTitle : function(title){
46825         this.title = title;
46826         if(this.region){
46827             this.region.updatePanelTitle(this, title);
46828         }
46829     },
46830     
46831     /**
46832      * Returns true is this panel was configured to be closable
46833      * @return {Boolean} 
46834      */
46835     isClosable : function(){
46836         return this.closable;
46837     },
46838     
46839     beforeSlide : function(){
46840         this.el.clip();
46841         this.resizeEl.clip();
46842     },
46843     
46844     afterSlide : function(){
46845         this.el.unclip();
46846         this.resizeEl.unclip();
46847     },
46848     
46849     /**
46850      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
46851      *   Will fail silently if the {@link #setUrl} method has not been called.
46852      *   This does not activate the panel, just updates its content.
46853      */
46854     refresh : function(){
46855         if(this.refreshDelegate){
46856            this.loaded = false;
46857            this.refreshDelegate();
46858         }
46859     },
46860     
46861     /**
46862      * Destroys this panel
46863      */
46864     destroy : function(){
46865         this.el.removeAllListeners();
46866         var tempEl = document.createElement("span");
46867         tempEl.appendChild(this.el.dom);
46868         tempEl.innerHTML = "";
46869         this.el.remove();
46870         this.el = null;
46871     },
46872     
46873     /**
46874      * form - if the content panel contains a form - this is a reference to it.
46875      * @type {Roo.form.Form}
46876      */
46877     form : false,
46878     /**
46879      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
46880      *    This contains a reference to it.
46881      * @type {Roo.View}
46882      */
46883     view : false,
46884     
46885       /**
46886      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
46887      * <pre><code>
46888
46889 layout.addxtype({
46890        xtype : 'Form',
46891        items: [ .... ]
46892    }
46893 );
46894
46895 </code></pre>
46896      * @param {Object} cfg Xtype definition of item to add.
46897      */
46898     
46899     addxtype : function(cfg) {
46900         // add form..
46901         if (cfg.xtype.match(/^Form$/)) {
46902             var el = this.el.createChild();
46903
46904             this.form = new  Roo.form.Form(cfg);
46905             
46906             
46907             if ( this.form.allItems.length) this.form.render(el.dom);
46908             return this.form;
46909         }
46910         // should only have one of theses..
46911         if (['View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
46912             // views..
46913             cfg.el = this.el.appendChild(document.createElement("div"));
46914             // factory?
46915             
46916             var ret = new Roo.factory(cfg);
46917             ret.render && ret.render(false, ''); // render blank..
46918             this.view = ret;
46919             return ret;
46920         }
46921         return false;
46922     }
46923 });
46924
46925 /**
46926  * @class Roo.GridPanel
46927  * @extends Roo.ContentPanel
46928  * @constructor
46929  * Create a new GridPanel.
46930  * @param {Roo.grid.Grid} grid The grid for this panel
46931  * @param {String/Object} config A string to set only the panel's title, or a config object
46932  */
46933 Roo.GridPanel = function(grid, config){
46934     
46935   
46936     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
46937         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
46938         
46939     this.wrapper.dom.appendChild(grid.getGridEl().dom);
46940     
46941     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
46942     
46943     if(this.toolbar){
46944         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
46945     }
46946     // xtype created footer. - not sure if will work as we normally have to render first..
46947     if (this.footer && !this.footer.el && this.footer.xtype) {
46948         
46949         this.footer.container = this.grid.getView().getFooterPanel(true);
46950         this.footer.dataSource = this.grid.dataSource;
46951         this.footer = Roo.factory(this.footer, Roo);
46952         
46953     }
46954     
46955     grid.monitorWindowResize = false; // turn off autosizing
46956     grid.autoHeight = false;
46957     grid.autoWidth = false;
46958     this.grid = grid;
46959     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
46960 };
46961
46962 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
46963     getId : function(){
46964         return this.grid.id;
46965     },
46966     
46967     /**
46968      * Returns the grid for this panel
46969      * @return {Roo.grid.Grid} 
46970      */
46971     getGrid : function(){
46972         return this.grid;    
46973     },
46974     
46975     setSize : function(width, height){
46976         if(!this.ignoreResize(width, height)){
46977             var grid = this.grid;
46978             var size = this.adjustForComponents(width, height);
46979             grid.getGridEl().setSize(size.width, size.height);
46980             grid.autoSize();
46981         }
46982     },
46983     
46984     beforeSlide : function(){
46985         this.grid.getView().scroller.clip();
46986     },
46987     
46988     afterSlide : function(){
46989         this.grid.getView().scroller.unclip();
46990     },
46991     
46992     destroy : function(){
46993         this.grid.destroy();
46994         delete this.grid;
46995         Roo.GridPanel.superclass.destroy.call(this); 
46996     }
46997 });
46998
46999
47000 /**
47001  * @class Roo.NestedLayoutPanel
47002  * @extends Roo.ContentPanel
47003  * @constructor
47004  * Create a new NestedLayoutPanel.
47005  * 
47006  * 
47007  * @param {Roo.BorderLayout} layout The layout for this panel
47008  * @param {String/Object} config A string to set only the title or a config object
47009  */
47010 Roo.NestedLayoutPanel = function(layout, config)
47011 {
47012     // construct with only one argument..
47013     /* FIXME - implement nicer consturctors
47014     if (layout.layout) {
47015         config = layout;
47016         layout = config.layout;
47017         delete config.layout;
47018     }
47019     if (layout.xtype && !layout.getEl) {
47020         // then layout needs constructing..
47021         layout = Roo.factory(layout, Roo);
47022     }
47023     */
47024     
47025     
47026     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
47027     
47028     layout.monitorWindowResize = false; // turn off autosizing
47029     this.layout = layout;
47030     this.layout.getEl().addClass("x-layout-nested-layout");
47031     
47032     
47033     
47034     
47035 };
47036
47037 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
47038
47039     setSize : function(width, height){
47040         if(!this.ignoreResize(width, height)){
47041             var size = this.adjustForComponents(width, height);
47042             var el = this.layout.getEl();
47043             el.setSize(size.width, size.height);
47044             var touch = el.dom.offsetWidth;
47045             this.layout.layout();
47046             // ie requires a double layout on the first pass
47047             if(Roo.isIE && !this.initialized){
47048                 this.initialized = true;
47049                 this.layout.layout();
47050             }
47051         }
47052     },
47053     
47054     // activate all subpanels if not currently active..
47055     
47056     setActiveState : function(active){
47057         this.active = active;
47058         if(!active){
47059             this.fireEvent("deactivate", this);
47060             return;
47061         }
47062         
47063         this.fireEvent("activate", this);
47064         // not sure if this should happen before or after..
47065         if (!this.layout) {
47066             return; // should not happen..
47067         }
47068         var reg = false;
47069         for (var r in this.layout.regions) {
47070             reg = this.layout.getRegion(r);
47071             if (reg.getActivePanel()) {
47072                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
47073                 reg.setActivePanel(reg.getActivePanel());
47074                 continue;
47075             }
47076             if (!reg.panels.length) {
47077                 continue;
47078             }
47079             reg.showPanel(reg.getPanel(0));
47080         }
47081         
47082         
47083         
47084         
47085     },
47086     
47087     /**
47088      * Returns the nested BorderLayout for this panel
47089      * @return {Roo.BorderLayout} 
47090      */
47091     getLayout : function(){
47092         return this.layout;
47093     },
47094     
47095      /**
47096      * Adds a xtype elements to the layout of the nested panel
47097      * <pre><code>
47098
47099 panel.addxtype({
47100        xtype : 'ContentPanel',
47101        region: 'west',
47102        items: [ .... ]
47103    }
47104 );
47105
47106 panel.addxtype({
47107         xtype : 'NestedLayoutPanel',
47108         region: 'west',
47109         layout: {
47110            center: { },
47111            west: { }   
47112         },
47113         items : [ ... list of content panels or nested layout panels.. ]
47114    }
47115 );
47116 </code></pre>
47117      * @param {Object} cfg Xtype definition of item to add.
47118      */
47119     addxtype : function(cfg) {
47120         return this.layout.addxtype(cfg);
47121     
47122     }
47123 });
47124
47125 Roo.ScrollPanel = function(el, config, content){
47126     config = config || {};
47127     config.fitToFrame = true;
47128     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
47129     
47130     this.el.dom.style.overflow = "hidden";
47131     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
47132     this.el.removeClass("x-layout-inactive-content");
47133     this.el.on("mousewheel", this.onWheel, this);
47134
47135     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
47136     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
47137     up.unselectable(); down.unselectable();
47138     up.on("click", this.scrollUp, this);
47139     down.on("click", this.scrollDown, this);
47140     up.addClassOnOver("x-scroller-btn-over");
47141     down.addClassOnOver("x-scroller-btn-over");
47142     up.addClassOnClick("x-scroller-btn-click");
47143     down.addClassOnClick("x-scroller-btn-click");
47144     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
47145
47146     this.resizeEl = this.el;
47147     this.el = wrap; this.up = up; this.down = down;
47148 };
47149
47150 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
47151     increment : 100,
47152     wheelIncrement : 5,
47153     scrollUp : function(){
47154         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
47155     },
47156
47157     scrollDown : function(){
47158         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
47159     },
47160
47161     afterScroll : function(){
47162         var el = this.resizeEl;
47163         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
47164         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
47165         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
47166     },
47167
47168     setSize : function(){
47169         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
47170         this.afterScroll();
47171     },
47172
47173     onWheel : function(e){
47174         var d = e.getWheelDelta();
47175         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
47176         this.afterScroll();
47177         e.stopEvent();
47178     },
47179
47180     setContent : function(content, loadScripts){
47181         this.resizeEl.update(content, loadScripts);
47182     }
47183
47184 });
47185
47186
47187
47188
47189
47190
47191
47192
47193
47194 /**
47195  * @class Roo.TreePanel
47196  * @extends Roo.ContentPanel
47197  * @constructor
47198  * Create a new TreePanel. - defaults to fit/scoll contents.
47199  * @param {String/Object} config A string to set only the panel's title, or a config object
47200  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
47201  */
47202 Roo.TreePanel = function(config){
47203     var el = config.el;
47204     var tree = config.tree;
47205     delete config.tree; 
47206     delete config.el; // hopefull!
47207     
47208     // wrapper for IE7 strict & safari scroll issue
47209     
47210     var treeEl = el.createChild();
47211     config.resizeEl = treeEl;
47212     
47213     
47214     
47215     Roo.TreePanel.superclass.constructor.call(this, el, config);
47216  
47217  
47218     this.tree = new Roo.tree.TreePanel(treeEl , tree);
47219     //console.log(tree);
47220     this.on('activate', function()
47221     {
47222         if (this.tree.rendered) {
47223             return;
47224         }
47225         //console.log('render tree');
47226         this.tree.render();
47227     });
47228     
47229     this.on('resize',  function (cp, w, h) {
47230             this.tree.innerCt.setWidth(w);
47231             this.tree.innerCt.setHeight(h);
47232             this.tree.innerCt.setStyle('overflow-y', 'auto');
47233     });
47234
47235         
47236     
47237 };
47238
47239 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
47240     fitToFrame : true,
47241     autoScroll : true
47242 });
47243
47244
47245
47246
47247
47248
47249
47250
47251
47252
47253
47254 /*
47255  * Based on:
47256  * Ext JS Library 1.1.1
47257  * Copyright(c) 2006-2007, Ext JS, LLC.
47258  *
47259  * Originally Released Under LGPL - original licence link has changed is not relivant.
47260  *
47261  * Fork - LGPL
47262  * <script type="text/javascript">
47263  */
47264  
47265
47266 /**
47267  * @class Roo.ReaderLayout
47268  * @extends Roo.BorderLayout
47269  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
47270  * center region containing two nested regions (a top one for a list view and one for item preview below),
47271  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
47272  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
47273  * expedites the setup of the overall layout and regions for this common application style.
47274  * Example:
47275  <pre><code>
47276 var reader = new Roo.ReaderLayout();
47277 var CP = Roo.ContentPanel;  // shortcut for adding
47278
47279 reader.beginUpdate();
47280 reader.add("north", new CP("north", "North"));
47281 reader.add("west", new CP("west", {title: "West"}));
47282 reader.add("east", new CP("east", {title: "East"}));
47283
47284 reader.regions.listView.add(new CP("listView", "List"));
47285 reader.regions.preview.add(new CP("preview", "Preview"));
47286 reader.endUpdate();
47287 </code></pre>
47288 * @constructor
47289 * Create a new ReaderLayout
47290 * @param {Object} config Configuration options
47291 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
47292 * document.body if omitted)
47293 */
47294 Roo.ReaderLayout = function(config, renderTo){
47295     var c = config || {size:{}};
47296     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
47297         north: c.north !== false ? Roo.apply({
47298             split:false,
47299             initialSize: 32,
47300             titlebar: false
47301         }, c.north) : false,
47302         west: c.west !== false ? Roo.apply({
47303             split:true,
47304             initialSize: 200,
47305             minSize: 175,
47306             maxSize: 400,
47307             titlebar: true,
47308             collapsible: true,
47309             animate: true,
47310             margins:{left:5,right:0,bottom:5,top:5},
47311             cmargins:{left:5,right:5,bottom:5,top:5}
47312         }, c.west) : false,
47313         east: c.east !== false ? Roo.apply({
47314             split:true,
47315             initialSize: 200,
47316             minSize: 175,
47317             maxSize: 400,
47318             titlebar: true,
47319             collapsible: true,
47320             animate: true,
47321             margins:{left:0,right:5,bottom:5,top:5},
47322             cmargins:{left:5,right:5,bottom:5,top:5}
47323         }, c.east) : false,
47324         center: Roo.apply({
47325             tabPosition: 'top',
47326             autoScroll:false,
47327             closeOnTab: true,
47328             titlebar:false,
47329             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
47330         }, c.center)
47331     });
47332
47333     this.el.addClass('x-reader');
47334
47335     this.beginUpdate();
47336
47337     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
47338         south: c.preview !== false ? Roo.apply({
47339             split:true,
47340             initialSize: 200,
47341             minSize: 100,
47342             autoScroll:true,
47343             collapsible:true,
47344             titlebar: true,
47345             cmargins:{top:5,left:0, right:0, bottom:0}
47346         }, c.preview) : false,
47347         center: Roo.apply({
47348             autoScroll:false,
47349             titlebar:false,
47350             minHeight:200
47351         }, c.listView)
47352     });
47353     this.add('center', new Roo.NestedLayoutPanel(inner,
47354             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
47355
47356     this.endUpdate();
47357
47358     this.regions.preview = inner.getRegion('south');
47359     this.regions.listView = inner.getRegion('center');
47360 };
47361
47362 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
47363  * Based on:
47364  * Ext JS Library 1.1.1
47365  * Copyright(c) 2006-2007, Ext JS, LLC.
47366  *
47367  * Originally Released Under LGPL - original licence link has changed is not relivant.
47368  *
47369  * Fork - LGPL
47370  * <script type="text/javascript">
47371  */
47372  
47373 /**
47374  * @class Roo.grid.Grid
47375  * @extends Roo.util.Observable
47376  * This class represents the primary interface of a component based grid control.
47377  * <br><br>Usage:<pre><code>
47378  var grid = new Roo.grid.Grid("my-container-id", {
47379      ds: myDataStore,
47380      cm: myColModel,
47381      selModel: mySelectionModel,
47382      autoSizeColumns: true,
47383      monitorWindowResize: false,
47384      trackMouseOver: true
47385  });
47386  // set any options
47387  grid.render();
47388  * </code></pre>
47389  * <b>Common Problems:</b><br/>
47390  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
47391  * element will correct this<br/>
47392  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
47393  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
47394  * are unpredictable.<br/>
47395  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
47396  * grid to calculate dimensions/offsets.<br/>
47397   * @constructor
47398  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
47399  * The container MUST have some type of size defined for the grid to fill. The container will be
47400  * automatically set to position relative if it isn't already.
47401  * @param {Object} config A config object that sets properties on this grid.
47402  */
47403 Roo.grid.Grid = function(container, config){
47404         // initialize the container
47405         this.container = Roo.get(container);
47406         this.container.update("");
47407         this.container.setStyle("overflow", "hidden");
47408     this.container.addClass('x-grid-container');
47409
47410     this.id = this.container.id;
47411
47412     Roo.apply(this, config);
47413     // check and correct shorthanded configs
47414     if(this.ds){
47415         this.dataSource = this.ds;
47416         delete this.ds;
47417     }
47418     if(this.cm){
47419         this.colModel = this.cm;
47420         delete this.cm;
47421     }
47422     if(this.sm){
47423         this.selModel = this.sm;
47424         delete this.sm;
47425     }
47426
47427     if (this.selModel) {
47428         this.selModel = Roo.factory(this.selModel, Roo.grid);
47429         this.sm = this.selModel;
47430         this.sm.xmodule = this.xmodule || false;
47431     }
47432     if (typeof(this.colModel.config) == 'undefined') {
47433         this.colModel = new Roo.grid.ColumnModel(this.colModel);
47434         this.cm = this.colModel;
47435         this.cm.xmodule = this.xmodule || false;
47436     }
47437     if (this.dataSource) {
47438         this.dataSource= Roo.factory(this.dataSource, Roo.data);
47439         this.ds = this.dataSource;
47440         this.ds.xmodule = this.xmodule || false;
47441          
47442     }
47443     
47444     
47445     
47446     if(this.width){
47447         this.container.setWidth(this.width);
47448     }
47449
47450     if(this.height){
47451         this.container.setHeight(this.height);
47452     }
47453     /** @private */
47454         this.addEvents({
47455         // raw events
47456         /**
47457          * @event click
47458          * The raw click event for the entire grid.
47459          * @param {Roo.EventObject} e
47460          */
47461         "click" : true,
47462         /**
47463          * @event dblclick
47464          * The raw dblclick event for the entire grid.
47465          * @param {Roo.EventObject} e
47466          */
47467         "dblclick" : true,
47468         /**
47469          * @event contextmenu
47470          * The raw contextmenu event for the entire grid.
47471          * @param {Roo.EventObject} e
47472          */
47473         "contextmenu" : true,
47474         /**
47475          * @event mousedown
47476          * The raw mousedown event for the entire grid.
47477          * @param {Roo.EventObject} e
47478          */
47479         "mousedown" : true,
47480         /**
47481          * @event mouseup
47482          * The raw mouseup event for the entire grid.
47483          * @param {Roo.EventObject} e
47484          */
47485         "mouseup" : true,
47486         /**
47487          * @event mouseover
47488          * The raw mouseover event for the entire grid.
47489          * @param {Roo.EventObject} e
47490          */
47491         "mouseover" : true,
47492         /**
47493          * @event mouseout
47494          * The raw mouseout event for the entire grid.
47495          * @param {Roo.EventObject} e
47496          */
47497         "mouseout" : true,
47498         /**
47499          * @event keypress
47500          * The raw keypress event for the entire grid.
47501          * @param {Roo.EventObject} e
47502          */
47503         "keypress" : true,
47504         /**
47505          * @event keydown
47506          * The raw keydown event for the entire grid.
47507          * @param {Roo.EventObject} e
47508          */
47509         "keydown" : true,
47510
47511         // custom events
47512
47513         /**
47514          * @event cellclick
47515          * Fires when a cell is clicked
47516          * @param {Grid} this
47517          * @param {Number} rowIndex
47518          * @param {Number} columnIndex
47519          * @param {Roo.EventObject} e
47520          */
47521         "cellclick" : true,
47522         /**
47523          * @event celldblclick
47524          * Fires when a cell is double clicked
47525          * @param {Grid} this
47526          * @param {Number} rowIndex
47527          * @param {Number} columnIndex
47528          * @param {Roo.EventObject} e
47529          */
47530         "celldblclick" : true,
47531         /**
47532          * @event rowclick
47533          * Fires when a row is clicked
47534          * @param {Grid} this
47535          * @param {Number} rowIndex
47536          * @param {Roo.EventObject} e
47537          */
47538         "rowclick" : true,
47539         /**
47540          * @event rowdblclick
47541          * Fires when a row is double clicked
47542          * @param {Grid} this
47543          * @param {Number} rowIndex
47544          * @param {Roo.EventObject} e
47545          */
47546         "rowdblclick" : true,
47547         /**
47548          * @event headerclick
47549          * Fires when a header is clicked
47550          * @param {Grid} this
47551          * @param {Number} columnIndex
47552          * @param {Roo.EventObject} e
47553          */
47554         "headerclick" : true,
47555         /**
47556          * @event headerdblclick
47557          * Fires when a header cell is double clicked
47558          * @param {Grid} this
47559          * @param {Number} columnIndex
47560          * @param {Roo.EventObject} e
47561          */
47562         "headerdblclick" : true,
47563         /**
47564          * @event rowcontextmenu
47565          * Fires when a row is right clicked
47566          * @param {Grid} this
47567          * @param {Number} rowIndex
47568          * @param {Roo.EventObject} e
47569          */
47570         "rowcontextmenu" : true,
47571         /**
47572          * @event cellcontextmenu
47573          * Fires when a cell is right clicked
47574          * @param {Grid} this
47575          * @param {Number} rowIndex
47576          * @param {Number} cellIndex
47577          * @param {Roo.EventObject} e
47578          */
47579          "cellcontextmenu" : true,
47580         /**
47581          * @event headercontextmenu
47582          * Fires when a header is right clicked
47583          * @param {Grid} this
47584          * @param {Number} columnIndex
47585          * @param {Roo.EventObject} e
47586          */
47587         "headercontextmenu" : true,
47588         /**
47589          * @event bodyscroll
47590          * Fires when the body element is scrolled
47591          * @param {Number} scrollLeft
47592          * @param {Number} scrollTop
47593          */
47594         "bodyscroll" : true,
47595         /**
47596          * @event columnresize
47597          * Fires when the user resizes a column
47598          * @param {Number} columnIndex
47599          * @param {Number} newSize
47600          */
47601         "columnresize" : true,
47602         /**
47603          * @event columnmove
47604          * Fires when the user moves a column
47605          * @param {Number} oldIndex
47606          * @param {Number} newIndex
47607          */
47608         "columnmove" : true,
47609         /**
47610          * @event startdrag
47611          * Fires when row(s) start being dragged
47612          * @param {Grid} this
47613          * @param {Roo.GridDD} dd The drag drop object
47614          * @param {event} e The raw browser event
47615          */
47616         "startdrag" : true,
47617         /**
47618          * @event enddrag
47619          * Fires when a drag operation is complete
47620          * @param {Grid} this
47621          * @param {Roo.GridDD} dd The drag drop object
47622          * @param {event} e The raw browser event
47623          */
47624         "enddrag" : true,
47625         /**
47626          * @event dragdrop
47627          * Fires when dragged row(s) are dropped on a valid DD target
47628          * @param {Grid} this
47629          * @param {Roo.GridDD} dd The drag drop object
47630          * @param {String} targetId The target drag drop object
47631          * @param {event} e The raw browser event
47632          */
47633         "dragdrop" : true,
47634         /**
47635          * @event dragover
47636          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
47637          * @param {Grid} this
47638          * @param {Roo.GridDD} dd The drag drop object
47639          * @param {String} targetId The target drag drop object
47640          * @param {event} e The raw browser event
47641          */
47642         "dragover" : true,
47643         /**
47644          * @event dragenter
47645          *  Fires when the dragged row(s) first cross another DD target while being dragged
47646          * @param {Grid} this
47647          * @param {Roo.GridDD} dd The drag drop object
47648          * @param {String} targetId The target drag drop object
47649          * @param {event} e The raw browser event
47650          */
47651         "dragenter" : true,
47652         /**
47653          * @event dragout
47654          * Fires when the dragged row(s) leave another DD target while being dragged
47655          * @param {Grid} this
47656          * @param {Roo.GridDD} dd The drag drop object
47657          * @param {String} targetId The target drag drop object
47658          * @param {event} e The raw browser event
47659          */
47660         "dragout" : true,
47661         /**
47662          * @event rowclass
47663          * Fires when a row is rendered, so you can change add a style to it.
47664          * @param {GridView} gridview   The grid view
47665          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
47666          */
47667         'rowclass' : true,
47668
47669         /**
47670          * @event render
47671          * Fires when the grid is rendered
47672          * @param {Grid} grid
47673          */
47674         'render' : true
47675     });
47676
47677     Roo.grid.Grid.superclass.constructor.call(this);
47678 };
47679 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
47680     
47681     /**
47682      * @cfg {String} ddGroup - drag drop group.
47683      */
47684
47685     /**
47686      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
47687      */
47688     minColumnWidth : 25,
47689
47690     /**
47691      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
47692      * <b>on initial render.</b> It is more efficient to explicitly size the columns
47693      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
47694      */
47695     autoSizeColumns : false,
47696
47697     /**
47698      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
47699      */
47700     autoSizeHeaders : true,
47701
47702     /**
47703      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
47704      */
47705     monitorWindowResize : true,
47706
47707     /**
47708      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
47709      * rows measured to get a columns size. Default is 0 (all rows).
47710      */
47711     maxRowsToMeasure : 0,
47712
47713     /**
47714      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
47715      */
47716     trackMouseOver : true,
47717
47718     /**
47719     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
47720     */
47721     
47722     /**
47723     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
47724     */
47725     enableDragDrop : false,
47726     
47727     /**
47728     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
47729     */
47730     enableColumnMove : true,
47731     
47732     /**
47733     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
47734     */
47735     enableColumnHide : true,
47736     
47737     /**
47738     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
47739     */
47740     enableRowHeightSync : false,
47741     
47742     /**
47743     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
47744     */
47745     stripeRows : true,
47746     
47747     /**
47748     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
47749     */
47750     autoHeight : false,
47751
47752     /**
47753      * @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.
47754      */
47755     autoExpandColumn : false,
47756
47757     /**
47758     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
47759     * Default is 50.
47760     */
47761     autoExpandMin : 50,
47762
47763     /**
47764     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
47765     */
47766     autoExpandMax : 1000,
47767
47768     /**
47769     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
47770     */
47771     view : null,
47772
47773     /**
47774     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
47775     */
47776     loadMask : false,
47777     /**
47778     * @cfg {Roo.dd.DropTarget} dragTarget An {@link Roo.dd.DragTarget} config
47779     */
47780     dropTarget: false,
47781     
47782    
47783     
47784     // private
47785     rendered : false,
47786
47787     /**
47788     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
47789     * of a fixed width. Default is false.
47790     */
47791     /**
47792     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
47793     */
47794     /**
47795      * Called once after all setup has been completed and the grid is ready to be rendered.
47796      * @return {Roo.grid.Grid} this
47797      */
47798     render : function()
47799     {
47800         var c = this.container;
47801         // try to detect autoHeight/width mode
47802         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
47803             this.autoHeight = true;
47804         }
47805         var view = this.getView();
47806         view.init(this);
47807
47808         c.on("click", this.onClick, this);
47809         c.on("dblclick", this.onDblClick, this);
47810         c.on("contextmenu", this.onContextMenu, this);
47811         c.on("keydown", this.onKeyDown, this);
47812
47813         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
47814
47815         this.getSelectionModel().init(this);
47816
47817         view.render();
47818
47819         if(this.loadMask){
47820             this.loadMask = new Roo.LoadMask(this.container,
47821                     Roo.apply({store:this.dataSource}, this.loadMask));
47822         }
47823         
47824         
47825         if (this.toolbar && this.toolbar.xtype) {
47826             this.toolbar.container = this.getView().getHeaderPanel(true);
47827             this.toolbar = new Roo.Toolbar(this.toolbar);
47828         }
47829         if (this.footer && this.footer.xtype) {
47830             this.footer.dataSource = this.getDataSource();
47831             this.footer.container = this.getView().getFooterPanel(true);
47832             this.footer = Roo.factory(this.footer, Roo);
47833         }
47834         if (this.dropTarget && this.dropTarget.xtype) {
47835             delete this.dropTarget.xtype;
47836             this.dropTarget =  new Ext.dd.DropTarget(this.getView().mainBody, this.dropTarget);
47837         }
47838         
47839         
47840         this.rendered = true;
47841         this.fireEvent('render', this);
47842         return this;
47843     },
47844
47845         /**
47846          * Reconfigures the grid to use a different Store and Column Model.
47847          * The View will be bound to the new objects and refreshed.
47848          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
47849          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
47850          */
47851     reconfigure : function(dataSource, colModel){
47852         if(this.loadMask){
47853             this.loadMask.destroy();
47854             this.loadMask = new Roo.LoadMask(this.container,
47855                     Roo.apply({store:dataSource}, this.loadMask));
47856         }
47857         this.view.bind(dataSource, colModel);
47858         this.dataSource = dataSource;
47859         this.colModel = colModel;
47860         this.view.refresh(true);
47861     },
47862
47863     // private
47864     onKeyDown : function(e){
47865         this.fireEvent("keydown", e);
47866     },
47867
47868     /**
47869      * Destroy this grid.
47870      * @param {Boolean} removeEl True to remove the element
47871      */
47872     destroy : function(removeEl, keepListeners){
47873         if(this.loadMask){
47874             this.loadMask.destroy();
47875         }
47876         var c = this.container;
47877         c.removeAllListeners();
47878         this.view.destroy();
47879         this.colModel.purgeListeners();
47880         if(!keepListeners){
47881             this.purgeListeners();
47882         }
47883         c.update("");
47884         if(removeEl === true){
47885             c.remove();
47886         }
47887     },
47888
47889     // private
47890     processEvent : function(name, e){
47891         this.fireEvent(name, e);
47892         var t = e.getTarget();
47893         var v = this.view;
47894         var header = v.findHeaderIndex(t);
47895         if(header !== false){
47896             this.fireEvent("header" + name, this, header, e);
47897         }else{
47898             var row = v.findRowIndex(t);
47899             var cell = v.findCellIndex(t);
47900             if(row !== false){
47901                 this.fireEvent("row" + name, this, row, e);
47902                 if(cell !== false){
47903                     this.fireEvent("cell" + name, this, row, cell, e);
47904                 }
47905             }
47906         }
47907     },
47908
47909     // private
47910     onClick : function(e){
47911         this.processEvent("click", e);
47912     },
47913
47914     // private
47915     onContextMenu : function(e, t){
47916         this.processEvent("contextmenu", e);
47917     },
47918
47919     // private
47920     onDblClick : function(e){
47921         this.processEvent("dblclick", e);
47922     },
47923
47924     // private
47925     walkCells : function(row, col, step, fn, scope){
47926         var cm = this.colModel, clen = cm.getColumnCount();
47927         var ds = this.dataSource, rlen = ds.getCount(), first = true;
47928         if(step < 0){
47929             if(col < 0){
47930                 row--;
47931                 first = false;
47932             }
47933             while(row >= 0){
47934                 if(!first){
47935                     col = clen-1;
47936                 }
47937                 first = false;
47938                 while(col >= 0){
47939                     if(fn.call(scope || this, row, col, cm) === true){
47940                         return [row, col];
47941                     }
47942                     col--;
47943                 }
47944                 row--;
47945             }
47946         } else {
47947             if(col >= clen){
47948                 row++;
47949                 first = false;
47950             }
47951             while(row < rlen){
47952                 if(!first){
47953                     col = 0;
47954                 }
47955                 first = false;
47956                 while(col < clen){
47957                     if(fn.call(scope || this, row, col, cm) === true){
47958                         return [row, col];
47959                     }
47960                     col++;
47961                 }
47962                 row++;
47963             }
47964         }
47965         return null;
47966     },
47967
47968     // private
47969     getSelections : function(){
47970         return this.selModel.getSelections();
47971     },
47972
47973     /**
47974      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
47975      * but if manual update is required this method will initiate it.
47976      */
47977     autoSize : function(){
47978         if(this.rendered){
47979             this.view.layout();
47980             if(this.view.adjustForScroll){
47981                 this.view.adjustForScroll();
47982             }
47983         }
47984     },
47985
47986     /**
47987      * Returns the grid's underlying element.
47988      * @return {Element} The element
47989      */
47990     getGridEl : function(){
47991         return this.container;
47992     },
47993
47994     // private for compatibility, overridden by editor grid
47995     stopEditing : function(){},
47996
47997     /**
47998      * Returns the grid's SelectionModel.
47999      * @return {SelectionModel}
48000      */
48001     getSelectionModel : function(){
48002         if(!this.selModel){
48003             this.selModel = new Roo.grid.RowSelectionModel();
48004         }
48005         return this.selModel;
48006     },
48007
48008     /**
48009      * Returns the grid's DataSource.
48010      * @return {DataSource}
48011      */
48012     getDataSource : function(){
48013         return this.dataSource;
48014     },
48015
48016     /**
48017      * Returns the grid's ColumnModel.
48018      * @return {ColumnModel}
48019      */
48020     getColumnModel : function(){
48021         return this.colModel;
48022     },
48023
48024     /**
48025      * Returns the grid's GridView object.
48026      * @return {GridView}
48027      */
48028     getView : function(){
48029         if(!this.view){
48030             this.view = new Roo.grid.GridView(this.viewConfig);
48031         }
48032         return this.view;
48033     },
48034     /**
48035      * Called to get grid's drag proxy text, by default returns this.ddText.
48036      * @return {String}
48037      */
48038     getDragDropText : function(){
48039         var count = this.selModel.getCount();
48040         return String.format(this.ddText, count, count == 1 ? '' : 's');
48041     }
48042 });
48043 /**
48044  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
48045  * %0 is replaced with the number of selected rows.
48046  * @type String
48047  */
48048 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
48049  * Based on:
48050  * Ext JS Library 1.1.1
48051  * Copyright(c) 2006-2007, Ext JS, LLC.
48052  *
48053  * Originally Released Under LGPL - original licence link has changed is not relivant.
48054  *
48055  * Fork - LGPL
48056  * <script type="text/javascript">
48057  */
48058  
48059 Roo.grid.AbstractGridView = function(){
48060         this.grid = null;
48061         
48062         this.events = {
48063             "beforerowremoved" : true,
48064             "beforerowsinserted" : true,
48065             "beforerefresh" : true,
48066             "rowremoved" : true,
48067             "rowsinserted" : true,
48068             "rowupdated" : true,
48069             "refresh" : true
48070         };
48071     Roo.grid.AbstractGridView.superclass.constructor.call(this);
48072 };
48073
48074 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
48075     rowClass : "x-grid-row",
48076     cellClass : "x-grid-cell",
48077     tdClass : "x-grid-td",
48078     hdClass : "x-grid-hd",
48079     splitClass : "x-grid-hd-split",
48080     
48081         init: function(grid){
48082         this.grid = grid;
48083                 var cid = this.grid.getGridEl().id;
48084         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
48085         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
48086         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
48087         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
48088         },
48089         
48090         getColumnRenderers : function(){
48091         var renderers = [];
48092         var cm = this.grid.colModel;
48093         var colCount = cm.getColumnCount();
48094         for(var i = 0; i < colCount; i++){
48095             renderers[i] = cm.getRenderer(i);
48096         }
48097         return renderers;
48098     },
48099     
48100     getColumnIds : function(){
48101         var ids = [];
48102         var cm = this.grid.colModel;
48103         var colCount = cm.getColumnCount();
48104         for(var i = 0; i < colCount; i++){
48105             ids[i] = cm.getColumnId(i);
48106         }
48107         return ids;
48108     },
48109     
48110     getDataIndexes : function(){
48111         if(!this.indexMap){
48112             this.indexMap = this.buildIndexMap();
48113         }
48114         return this.indexMap.colToData;
48115     },
48116     
48117     getColumnIndexByDataIndex : function(dataIndex){
48118         if(!this.indexMap){
48119             this.indexMap = this.buildIndexMap();
48120         }
48121         return this.indexMap.dataToCol[dataIndex];
48122     },
48123     
48124     /**
48125      * Set a css style for a column dynamically. 
48126      * @param {Number} colIndex The index of the column
48127      * @param {String} name The css property name
48128      * @param {String} value The css value
48129      */
48130     setCSSStyle : function(colIndex, name, value){
48131         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
48132         Roo.util.CSS.updateRule(selector, name, value);
48133     },
48134     
48135     generateRules : function(cm){
48136         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
48137         Roo.util.CSS.removeStyleSheet(rulesId);
48138         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48139             var cid = cm.getColumnId(i);
48140             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
48141                          this.tdSelector, cid, " {\n}\n",
48142                          this.hdSelector, cid, " {\n}\n",
48143                          this.splitSelector, cid, " {\n}\n");
48144         }
48145         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
48146     }
48147 });/*
48148  * Based on:
48149  * Ext JS Library 1.1.1
48150  * Copyright(c) 2006-2007, Ext JS, LLC.
48151  *
48152  * Originally Released Under LGPL - original licence link has changed is not relivant.
48153  *
48154  * Fork - LGPL
48155  * <script type="text/javascript">
48156  */
48157
48158 // private
48159 // This is a support class used internally by the Grid components
48160 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
48161     this.grid = grid;
48162     this.view = grid.getView();
48163     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
48164     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
48165     if(hd2){
48166         this.setHandleElId(Roo.id(hd));
48167         this.setOuterHandleElId(Roo.id(hd2));
48168     }
48169     this.scroll = false;
48170 };
48171 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
48172     maxDragWidth: 120,
48173     getDragData : function(e){
48174         var t = Roo.lib.Event.getTarget(e);
48175         var h = this.view.findHeaderCell(t);
48176         if(h){
48177             return {ddel: h.firstChild, header:h};
48178         }
48179         return false;
48180     },
48181
48182     onInitDrag : function(e){
48183         this.view.headersDisabled = true;
48184         var clone = this.dragData.ddel.cloneNode(true);
48185         clone.id = Roo.id();
48186         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
48187         this.proxy.update(clone);
48188         return true;
48189     },
48190
48191     afterValidDrop : function(){
48192         var v = this.view;
48193         setTimeout(function(){
48194             v.headersDisabled = false;
48195         }, 50);
48196     },
48197
48198     afterInvalidDrop : function(){
48199         var v = this.view;
48200         setTimeout(function(){
48201             v.headersDisabled = false;
48202         }, 50);
48203     }
48204 });
48205 /*
48206  * Based on:
48207  * Ext JS Library 1.1.1
48208  * Copyright(c) 2006-2007, Ext JS, LLC.
48209  *
48210  * Originally Released Under LGPL - original licence link has changed is not relivant.
48211  *
48212  * Fork - LGPL
48213  * <script type="text/javascript">
48214  */
48215 // private
48216 // This is a support class used internally by the Grid components
48217 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
48218     this.grid = grid;
48219     this.view = grid.getView();
48220     // split the proxies so they don't interfere with mouse events
48221     this.proxyTop = Roo.DomHelper.append(document.body, {
48222         cls:"col-move-top", html:"&#160;"
48223     }, true);
48224     this.proxyBottom = Roo.DomHelper.append(document.body, {
48225         cls:"col-move-bottom", html:"&#160;"
48226     }, true);
48227     this.proxyTop.hide = this.proxyBottom.hide = function(){
48228         this.setLeftTop(-100,-100);
48229         this.setStyle("visibility", "hidden");
48230     };
48231     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
48232     // temporarily disabled
48233     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
48234     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
48235 };
48236 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
48237     proxyOffsets : [-4, -9],
48238     fly: Roo.Element.fly,
48239
48240     getTargetFromEvent : function(e){
48241         var t = Roo.lib.Event.getTarget(e);
48242         var cindex = this.view.findCellIndex(t);
48243         if(cindex !== false){
48244             return this.view.getHeaderCell(cindex);
48245         }
48246         return null;
48247     },
48248
48249     nextVisible : function(h){
48250         var v = this.view, cm = this.grid.colModel;
48251         h = h.nextSibling;
48252         while(h){
48253             if(!cm.isHidden(v.getCellIndex(h))){
48254                 return h;
48255             }
48256             h = h.nextSibling;
48257         }
48258         return null;
48259     },
48260
48261     prevVisible : function(h){
48262         var v = this.view, cm = this.grid.colModel;
48263         h = h.prevSibling;
48264         while(h){
48265             if(!cm.isHidden(v.getCellIndex(h))){
48266                 return h;
48267             }
48268             h = h.prevSibling;
48269         }
48270         return null;
48271     },
48272
48273     positionIndicator : function(h, n, e){
48274         var x = Roo.lib.Event.getPageX(e);
48275         var r = Roo.lib.Dom.getRegion(n.firstChild);
48276         var px, pt, py = r.top + this.proxyOffsets[1];
48277         if((r.right - x) <= (r.right-r.left)/2){
48278             px = r.right+this.view.borderWidth;
48279             pt = "after";
48280         }else{
48281             px = r.left;
48282             pt = "before";
48283         }
48284         var oldIndex = this.view.getCellIndex(h);
48285         var newIndex = this.view.getCellIndex(n);
48286
48287         if(this.grid.colModel.isFixed(newIndex)){
48288             return false;
48289         }
48290
48291         var locked = this.grid.colModel.isLocked(newIndex);
48292
48293         if(pt == "after"){
48294             newIndex++;
48295         }
48296         if(oldIndex < newIndex){
48297             newIndex--;
48298         }
48299         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
48300             return false;
48301         }
48302         px +=  this.proxyOffsets[0];
48303         this.proxyTop.setLeftTop(px, py);
48304         this.proxyTop.show();
48305         if(!this.bottomOffset){
48306             this.bottomOffset = this.view.mainHd.getHeight();
48307         }
48308         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
48309         this.proxyBottom.show();
48310         return pt;
48311     },
48312
48313     onNodeEnter : function(n, dd, e, data){
48314         if(data.header != n){
48315             this.positionIndicator(data.header, n, e);
48316         }
48317     },
48318
48319     onNodeOver : function(n, dd, e, data){
48320         var result = false;
48321         if(data.header != n){
48322             result = this.positionIndicator(data.header, n, e);
48323         }
48324         if(!result){
48325             this.proxyTop.hide();
48326             this.proxyBottom.hide();
48327         }
48328         return result ? this.dropAllowed : this.dropNotAllowed;
48329     },
48330
48331     onNodeOut : function(n, dd, e, data){
48332         this.proxyTop.hide();
48333         this.proxyBottom.hide();
48334     },
48335
48336     onNodeDrop : function(n, dd, e, data){
48337         var h = data.header;
48338         if(h != n){
48339             var cm = this.grid.colModel;
48340             var x = Roo.lib.Event.getPageX(e);
48341             var r = Roo.lib.Dom.getRegion(n.firstChild);
48342             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
48343             var oldIndex = this.view.getCellIndex(h);
48344             var newIndex = this.view.getCellIndex(n);
48345             var locked = cm.isLocked(newIndex);
48346             if(pt == "after"){
48347                 newIndex++;
48348             }
48349             if(oldIndex < newIndex){
48350                 newIndex--;
48351             }
48352             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
48353                 return false;
48354             }
48355             cm.setLocked(oldIndex, locked, true);
48356             cm.moveColumn(oldIndex, newIndex);
48357             this.grid.fireEvent("columnmove", oldIndex, newIndex);
48358             return true;
48359         }
48360         return false;
48361     }
48362 });
48363 /*
48364  * Based on:
48365  * Ext JS Library 1.1.1
48366  * Copyright(c) 2006-2007, Ext JS, LLC.
48367  *
48368  * Originally Released Under LGPL - original licence link has changed is not relivant.
48369  *
48370  * Fork - LGPL
48371  * <script type="text/javascript">
48372  */
48373   
48374 /**
48375  * @class Roo.grid.GridView
48376  * @extends Roo.util.Observable
48377  *
48378  * @constructor
48379  * @param {Object} config
48380  */
48381 Roo.grid.GridView = function(config){
48382     Roo.grid.GridView.superclass.constructor.call(this);
48383     this.el = null;
48384
48385     Roo.apply(this, config);
48386 };
48387
48388 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
48389
48390     /**
48391      * Override this function to apply custom css classes to rows during rendering
48392      * @param {Record} record The record
48393      * @param {Number} index
48394      * @method getRowClass
48395      */
48396     rowClass : "x-grid-row",
48397
48398     cellClass : "x-grid-col",
48399
48400     tdClass : "x-grid-td",
48401
48402     hdClass : "x-grid-hd",
48403
48404     splitClass : "x-grid-split",
48405
48406     sortClasses : ["sort-asc", "sort-desc"],
48407
48408     enableMoveAnim : false,
48409
48410     hlColor: "C3DAF9",
48411
48412     dh : Roo.DomHelper,
48413
48414     fly : Roo.Element.fly,
48415
48416     css : Roo.util.CSS,
48417
48418     borderWidth: 1,
48419
48420     splitOffset: 3,
48421
48422     scrollIncrement : 22,
48423
48424     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
48425
48426     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
48427
48428     bind : function(ds, cm){
48429         if(this.ds){
48430             this.ds.un("load", this.onLoad, this);
48431             this.ds.un("datachanged", this.onDataChange, this);
48432             this.ds.un("add", this.onAdd, this);
48433             this.ds.un("remove", this.onRemove, this);
48434             this.ds.un("update", this.onUpdate, this);
48435             this.ds.un("clear", this.onClear, this);
48436         }
48437         if(ds){
48438             ds.on("load", this.onLoad, this);
48439             ds.on("datachanged", this.onDataChange, this);
48440             ds.on("add", this.onAdd, this);
48441             ds.on("remove", this.onRemove, this);
48442             ds.on("update", this.onUpdate, this);
48443             ds.on("clear", this.onClear, this);
48444         }
48445         this.ds = ds;
48446
48447         if(this.cm){
48448             this.cm.un("widthchange", this.onColWidthChange, this);
48449             this.cm.un("headerchange", this.onHeaderChange, this);
48450             this.cm.un("hiddenchange", this.onHiddenChange, this);
48451             this.cm.un("columnmoved", this.onColumnMove, this);
48452             this.cm.un("columnlockchange", this.onColumnLock, this);
48453         }
48454         if(cm){
48455             this.generateRules(cm);
48456             cm.on("widthchange", this.onColWidthChange, this);
48457             cm.on("headerchange", this.onHeaderChange, this);
48458             cm.on("hiddenchange", this.onHiddenChange, this);
48459             cm.on("columnmoved", this.onColumnMove, this);
48460             cm.on("columnlockchange", this.onColumnLock, this);
48461         }
48462         this.cm = cm;
48463     },
48464
48465     init: function(grid){
48466         Roo.grid.GridView.superclass.init.call(this, grid);
48467
48468         this.bind(grid.dataSource, grid.colModel);
48469
48470         grid.on("headerclick", this.handleHeaderClick, this);
48471
48472         if(grid.trackMouseOver){
48473             grid.on("mouseover", this.onRowOver, this);
48474             grid.on("mouseout", this.onRowOut, this);
48475         }
48476         grid.cancelTextSelection = function(){};
48477         this.gridId = grid.id;
48478
48479         var tpls = this.templates || {};
48480
48481         if(!tpls.master){
48482             tpls.master = new Roo.Template(
48483                '<div class="x-grid" hidefocus="true">',
48484                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
48485                   '<div class="x-grid-topbar"></div>',
48486                   '<div class="x-grid-scroller"><div></div></div>',
48487                   '<div class="x-grid-locked">',
48488                       '<div class="x-grid-header">{lockedHeader}</div>',
48489                       '<div class="x-grid-body">{lockedBody}</div>',
48490                   "</div>",
48491                   '<div class="x-grid-viewport">',
48492                       '<div class="x-grid-header">{header}</div>',
48493                       '<div class="x-grid-body">{body}</div>',
48494                   "</div>",
48495                   '<div class="x-grid-bottombar"></div>',
48496                  
48497                   '<div class="x-grid-resize-proxy">&#160;</div>',
48498                "</div>"
48499             );
48500             tpls.master.disableformats = true;
48501         }
48502
48503         if(!tpls.header){
48504             tpls.header = new Roo.Template(
48505                '<table border="0" cellspacing="0" cellpadding="0">',
48506                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
48507                "</table>{splits}"
48508             );
48509             tpls.header.disableformats = true;
48510         }
48511         tpls.header.compile();
48512
48513         if(!tpls.hcell){
48514             tpls.hcell = new Roo.Template(
48515                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
48516                 '<div class="x-grid-hd-text" unselectable="on">{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
48517                 "</div></td>"
48518              );
48519              tpls.hcell.disableFormats = true;
48520         }
48521         tpls.hcell.compile();
48522
48523         if(!tpls.hsplit){
48524             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style}" unselectable="on">&#160;</div>');
48525             tpls.hsplit.disableFormats = true;
48526         }
48527         tpls.hsplit.compile();
48528
48529         if(!tpls.body){
48530             tpls.body = new Roo.Template(
48531                '<table border="0" cellspacing="0" cellpadding="0">',
48532                "<tbody>{rows}</tbody>",
48533                "</table>"
48534             );
48535             tpls.body.disableFormats = true;
48536         }
48537         tpls.body.compile();
48538
48539         if(!tpls.row){
48540             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
48541             tpls.row.disableFormats = true;
48542         }
48543         tpls.row.compile();
48544
48545         if(!tpls.cell){
48546             tpls.cell = new Roo.Template(
48547                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
48548                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text" unselectable="on" {attr}>{value}</div></div>',
48549                 "</td>"
48550             );
48551             tpls.cell.disableFormats = true;
48552         }
48553         tpls.cell.compile();
48554
48555         this.templates = tpls;
48556     },
48557
48558     // remap these for backwards compat
48559     onColWidthChange : function(){
48560         this.updateColumns.apply(this, arguments);
48561     },
48562     onHeaderChange : function(){
48563         this.updateHeaders.apply(this, arguments);
48564     }, 
48565     onHiddenChange : function(){
48566         this.handleHiddenChange.apply(this, arguments);
48567     },
48568     onColumnMove : function(){
48569         this.handleColumnMove.apply(this, arguments);
48570     },
48571     onColumnLock : function(){
48572         this.handleLockChange.apply(this, arguments);
48573     },
48574
48575     onDataChange : function(){
48576         this.refresh();
48577         this.updateHeaderSortState();
48578     },
48579
48580     onClear : function(){
48581         this.refresh();
48582     },
48583
48584     onUpdate : function(ds, record){
48585         this.refreshRow(record);
48586     },
48587
48588     refreshRow : function(record){
48589         var ds = this.ds, index;
48590         if(typeof record == 'number'){
48591             index = record;
48592             record = ds.getAt(index);
48593         }else{
48594             index = ds.indexOf(record);
48595         }
48596         this.insertRows(ds, index, index, true);
48597         this.onRemove(ds, record, index+1, true);
48598         this.syncRowHeights(index, index);
48599         this.layout();
48600         this.fireEvent("rowupdated", this, index, record);
48601     },
48602
48603     onAdd : function(ds, records, index){
48604         this.insertRows(ds, index, index + (records.length-1));
48605     },
48606
48607     onRemove : function(ds, record, index, isUpdate){
48608         if(isUpdate !== true){
48609             this.fireEvent("beforerowremoved", this, index, record);
48610         }
48611         var bt = this.getBodyTable(), lt = this.getLockedTable();
48612         if(bt.rows[index]){
48613             bt.firstChild.removeChild(bt.rows[index]);
48614         }
48615         if(lt.rows[index]){
48616             lt.firstChild.removeChild(lt.rows[index]);
48617         }
48618         if(isUpdate !== true){
48619             this.stripeRows(index);
48620             this.syncRowHeights(index, index);
48621             this.layout();
48622             this.fireEvent("rowremoved", this, index, record);
48623         }
48624     },
48625
48626     onLoad : function(){
48627         this.scrollToTop();
48628     },
48629
48630     /**
48631      * Scrolls the grid to the top
48632      */
48633     scrollToTop : function(){
48634         if(this.scroller){
48635             this.scroller.dom.scrollTop = 0;
48636             this.syncScroll();
48637         }
48638     },
48639
48640     /**
48641      * Gets a panel in the header of the grid that can be used for toolbars etc.
48642      * After modifying the contents of this panel a call to grid.autoSize() may be
48643      * required to register any changes in size.
48644      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
48645      * @return Roo.Element
48646      */
48647     getHeaderPanel : function(doShow){
48648         if(doShow){
48649             this.headerPanel.show();
48650         }
48651         return this.headerPanel;
48652     },
48653
48654     /**
48655      * Gets a panel in the footer of the grid that can be used for toolbars etc.
48656      * After modifying the contents of this panel a call to grid.autoSize() may be
48657      * required to register any changes in size.
48658      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
48659      * @return Roo.Element
48660      */
48661     getFooterPanel : function(doShow){
48662         if(doShow){
48663             this.footerPanel.show();
48664         }
48665         return this.footerPanel;
48666     },
48667
48668     initElements : function(){
48669         var E = Roo.Element;
48670         var el = this.grid.getGridEl().dom.firstChild;
48671         var cs = el.childNodes;
48672
48673         this.el = new E(el);
48674         
48675          this.focusEl = new E(el.firstChild);
48676         this.focusEl.swallowEvent("click", true);
48677         
48678         this.headerPanel = new E(cs[1]);
48679         this.headerPanel.enableDisplayMode("block");
48680
48681         this.scroller = new E(cs[2]);
48682         this.scrollSizer = new E(this.scroller.dom.firstChild);
48683
48684         this.lockedWrap = new E(cs[3]);
48685         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
48686         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
48687
48688         this.mainWrap = new E(cs[4]);
48689         this.mainHd = new E(this.mainWrap.dom.firstChild);
48690         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
48691
48692         this.footerPanel = new E(cs[5]);
48693         this.footerPanel.enableDisplayMode("block");
48694
48695         this.resizeProxy = new E(cs[6]);
48696
48697         this.headerSelector = String.format(
48698            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
48699            this.lockedHd.id, this.mainHd.id
48700         );
48701
48702         this.splitterSelector = String.format(
48703            '#{0} div.x-grid-split, #{1} div.x-grid-split',
48704            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
48705         );
48706     },
48707     idToCssName : function(s)
48708     {
48709         return s.replace(/[^a-z0-9]+/ig, '-');
48710     },
48711
48712     getHeaderCell : function(index){
48713         return Roo.DomQuery.select(this.headerSelector)[index];
48714     },
48715
48716     getHeaderCellMeasure : function(index){
48717         return this.getHeaderCell(index).firstChild;
48718     },
48719
48720     getHeaderCellText : function(index){
48721         return this.getHeaderCell(index).firstChild.firstChild;
48722     },
48723
48724     getLockedTable : function(){
48725         return this.lockedBody.dom.firstChild;
48726     },
48727
48728     getBodyTable : function(){
48729         return this.mainBody.dom.firstChild;
48730     },
48731
48732     getLockedRow : function(index){
48733         return this.getLockedTable().rows[index];
48734     },
48735
48736     getRow : function(index){
48737         return this.getBodyTable().rows[index];
48738     },
48739
48740     getRowComposite : function(index){
48741         if(!this.rowEl){
48742             this.rowEl = new Roo.CompositeElementLite();
48743         }
48744         var els = [], lrow, mrow;
48745         if(lrow = this.getLockedRow(index)){
48746             els.push(lrow);
48747         }
48748         if(mrow = this.getRow(index)){
48749             els.push(mrow);
48750         }
48751         this.rowEl.elements = els;
48752         return this.rowEl;
48753     },
48754     /**
48755      * Gets the 'td' of the cell
48756      * 
48757      * @param {Integer} rowIndex row to select
48758      * @param {Integer} colIndex column to select
48759      * 
48760      * @return {Object} 
48761      */
48762     getCell : function(rowIndex, colIndex){
48763         var locked = this.cm.getLockedCount();
48764         var source;
48765         if(colIndex < locked){
48766             source = this.lockedBody.dom.firstChild;
48767         }else{
48768             source = this.mainBody.dom.firstChild;
48769             colIndex -= locked;
48770         }
48771         return source.rows[rowIndex].childNodes[colIndex];
48772     },
48773
48774     getCellText : function(rowIndex, colIndex){
48775         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
48776     },
48777
48778     getCellBox : function(cell){
48779         var b = this.fly(cell).getBox();
48780         if(Roo.isOpera){ // opera fails to report the Y
48781             b.y = cell.offsetTop + this.mainBody.getY();
48782         }
48783         return b;
48784     },
48785
48786     getCellIndex : function(cell){
48787         var id = String(cell.className).match(this.cellRE);
48788         if(id){
48789             return parseInt(id[1], 10);
48790         }
48791         return 0;
48792     },
48793
48794     findHeaderIndex : function(n){
48795         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
48796         return r ? this.getCellIndex(r) : false;
48797     },
48798
48799     findHeaderCell : function(n){
48800         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
48801         return r ? r : false;
48802     },
48803
48804     findRowIndex : function(n){
48805         if(!n){
48806             return false;
48807         }
48808         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
48809         return r ? r.rowIndex : false;
48810     },
48811
48812     findCellIndex : function(node){
48813         var stop = this.el.dom;
48814         while(node && node != stop){
48815             if(this.findRE.test(node.className)){
48816                 return this.getCellIndex(node);
48817             }
48818             node = node.parentNode;
48819         }
48820         return false;
48821     },
48822
48823     getColumnId : function(index){
48824         return this.cm.getColumnId(index);
48825     },
48826
48827     getSplitters : function()
48828     {
48829         if(this.splitterSelector){
48830            return Roo.DomQuery.select(this.splitterSelector);
48831         }else{
48832             return null;
48833       }
48834     },
48835
48836     getSplitter : function(index){
48837         return this.getSplitters()[index];
48838     },
48839
48840     onRowOver : function(e, t){
48841         var row;
48842         if((row = this.findRowIndex(t)) !== false){
48843             this.getRowComposite(row).addClass("x-grid-row-over");
48844         }
48845     },
48846
48847     onRowOut : function(e, t){
48848         var row;
48849         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
48850             this.getRowComposite(row).removeClass("x-grid-row-over");
48851         }
48852     },
48853
48854     renderHeaders : function(){
48855         var cm = this.cm;
48856         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
48857         var cb = [], lb = [], sb = [], lsb = [], p = {};
48858         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48859             p.cellId = "x-grid-hd-0-" + i;
48860             p.splitId = "x-grid-csplit-0-" + i;
48861             p.id = cm.getColumnId(i);
48862             p.title = cm.getColumnTooltip(i) || "";
48863             p.value = cm.getColumnHeader(i) || "";
48864             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
48865             if(!cm.isLocked(i)){
48866                 cb[cb.length] = ct.apply(p);
48867                 sb[sb.length] = st.apply(p);
48868             }else{
48869                 lb[lb.length] = ct.apply(p);
48870                 lsb[lsb.length] = st.apply(p);
48871             }
48872         }
48873         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
48874                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
48875     },
48876
48877     updateHeaders : function(){
48878         var html = this.renderHeaders();
48879         this.lockedHd.update(html[0]);
48880         this.mainHd.update(html[1]);
48881     },
48882
48883     /**
48884      * Focuses the specified row.
48885      * @param {Number} row The row index
48886      */
48887     focusRow : function(row)
48888     {
48889         //Roo.log('GridView.focusRow');
48890         var x = this.scroller.dom.scrollLeft;
48891         this.focusCell(row, 0, false);
48892         this.scroller.dom.scrollLeft = x;
48893     },
48894
48895     /**
48896      * Focuses the specified cell.
48897      * @param {Number} row The row index
48898      * @param {Number} col The column index
48899      * @param {Boolean} hscroll false to disable horizontal scrolling
48900      */
48901     focusCell : function(row, col, hscroll)
48902     {
48903         //Roo.log('GridView.focusCell');
48904         var el = this.ensureVisible(row, col, hscroll);
48905         this.focusEl.alignTo(el, "tl-tl");
48906         if(Roo.isGecko){
48907             this.focusEl.focus();
48908         }else{
48909             this.focusEl.focus.defer(1, this.focusEl);
48910         }
48911     },
48912
48913     /**
48914      * Scrolls the specified cell into view
48915      * @param {Number} row The row index
48916      * @param {Number} col The column index
48917      * @param {Boolean} hscroll false to disable horizontal scrolling
48918      */
48919     ensureVisible : function(row, col, hscroll)
48920     {
48921         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
48922         //return null; //disable for testing.
48923         if(typeof row != "number"){
48924             row = row.rowIndex;
48925         }
48926         if(row < 0 && row >= this.ds.getCount()){
48927             return  null;
48928         }
48929         col = (col !== undefined ? col : 0);
48930         var cm = this.grid.colModel;
48931         while(cm.isHidden(col)){
48932             col++;
48933         }
48934
48935         var el = this.getCell(row, col);
48936         if(!el){
48937             return null;
48938         }
48939         var c = this.scroller.dom;
48940
48941         var ctop = parseInt(el.offsetTop, 10);
48942         var cleft = parseInt(el.offsetLeft, 10);
48943         var cbot = ctop + el.offsetHeight;
48944         var cright = cleft + el.offsetWidth;
48945         
48946         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
48947         var stop = parseInt(c.scrollTop, 10);
48948         var sleft = parseInt(c.scrollLeft, 10);
48949         var sbot = stop + ch;
48950         var sright = sleft + c.clientWidth;
48951         /*
48952         Roo.log('GridView.ensureVisible:' +
48953                 ' ctop:' + ctop +
48954                 ' c.clientHeight:' + c.clientHeight +
48955                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
48956                 ' stop:' + stop +
48957                 ' cbot:' + cbot +
48958                 ' sbot:' + sbot +
48959                 ' ch:' + ch  
48960                 );
48961         */
48962         if(ctop < stop){
48963              c.scrollTop = ctop;
48964             //Roo.log("set scrolltop to ctop DISABLE?");
48965         }else if(cbot > sbot){
48966             //Roo.log("set scrolltop to cbot-ch");
48967             c.scrollTop = cbot-ch;
48968         }
48969         
48970         if(hscroll !== false){
48971             if(cleft < sleft){
48972                 c.scrollLeft = cleft;
48973             }else if(cright > sright){
48974                 c.scrollLeft = cright-c.clientWidth;
48975             }
48976         }
48977          
48978         return el;
48979     },
48980
48981     updateColumns : function(){
48982         this.grid.stopEditing();
48983         var cm = this.grid.colModel, colIds = this.getColumnIds();
48984         //var totalWidth = cm.getTotalWidth();
48985         var pos = 0;
48986         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48987             //if(cm.isHidden(i)) continue;
48988             var w = cm.getColumnWidth(i);
48989             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
48990             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
48991         }
48992         this.updateSplitters();
48993     },
48994
48995     generateRules : function(cm){
48996         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
48997         Roo.util.CSS.removeStyleSheet(rulesId);
48998         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48999             var cid = cm.getColumnId(i);
49000             var align = '';
49001             if(cm.config[i].align){
49002                 align = 'text-align:'+cm.config[i].align+';';
49003             }
49004             var hidden = '';
49005             if(cm.isHidden(i)){
49006                 hidden = 'display:none;';
49007             }
49008             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
49009             ruleBuf.push(
49010                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
49011                     this.hdSelector, cid, " {\n", align, width, "}\n",
49012                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
49013                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
49014         }
49015         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
49016     },
49017
49018     updateSplitters : function(){
49019         var cm = this.cm, s = this.getSplitters();
49020         if(s){ // splitters not created yet
49021             var pos = 0, locked = true;
49022             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
49023                 if(cm.isHidden(i)) continue;
49024                 var w = cm.getColumnWidth(i); // make sure it's a number
49025                 if(!cm.isLocked(i) && locked){
49026                     pos = 0;
49027                     locked = false;
49028                 }
49029                 pos += w;
49030                 s[i].style.left = (pos-this.splitOffset) + "px";
49031             }
49032         }
49033     },
49034
49035     handleHiddenChange : function(colModel, colIndex, hidden){
49036         if(hidden){
49037             this.hideColumn(colIndex);
49038         }else{
49039             this.unhideColumn(colIndex);
49040         }
49041     },
49042
49043     hideColumn : function(colIndex){
49044         var cid = this.getColumnId(colIndex);
49045         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
49046         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
49047         if(Roo.isSafari){
49048             this.updateHeaders();
49049         }
49050         this.updateSplitters();
49051         this.layout();
49052     },
49053
49054     unhideColumn : function(colIndex){
49055         var cid = this.getColumnId(colIndex);
49056         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
49057         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
49058
49059         if(Roo.isSafari){
49060             this.updateHeaders();
49061         }
49062         this.updateSplitters();
49063         this.layout();
49064     },
49065
49066     insertRows : function(dm, firstRow, lastRow, isUpdate){
49067         if(firstRow == 0 && lastRow == dm.getCount()-1){
49068             this.refresh();
49069         }else{
49070             if(!isUpdate){
49071                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
49072             }
49073             var s = this.getScrollState();
49074             var markup = this.renderRows(firstRow, lastRow);
49075             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
49076             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
49077             this.restoreScroll(s);
49078             if(!isUpdate){
49079                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
49080                 this.syncRowHeights(firstRow, lastRow);
49081                 this.stripeRows(firstRow);
49082                 this.layout();
49083             }
49084         }
49085     },
49086
49087     bufferRows : function(markup, target, index){
49088         var before = null, trows = target.rows, tbody = target.tBodies[0];
49089         if(index < trows.length){
49090             before = trows[index];
49091         }
49092         var b = document.createElement("div");
49093         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
49094         var rows = b.firstChild.rows;
49095         for(var i = 0, len = rows.length; i < len; i++){
49096             if(before){
49097                 tbody.insertBefore(rows[0], before);
49098             }else{
49099                 tbody.appendChild(rows[0]);
49100             }
49101         }
49102         b.innerHTML = "";
49103         b = null;
49104     },
49105
49106     deleteRows : function(dm, firstRow, lastRow){
49107         if(dm.getRowCount()<1){
49108             this.fireEvent("beforerefresh", this);
49109             this.mainBody.update("");
49110             this.lockedBody.update("");
49111             this.fireEvent("refresh", this);
49112         }else{
49113             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
49114             var bt = this.getBodyTable();
49115             var tbody = bt.firstChild;
49116             var rows = bt.rows;
49117             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
49118                 tbody.removeChild(rows[firstRow]);
49119             }
49120             this.stripeRows(firstRow);
49121             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
49122         }
49123     },
49124
49125     updateRows : function(dataSource, firstRow, lastRow){
49126         var s = this.getScrollState();
49127         this.refresh();
49128         this.restoreScroll(s);
49129     },
49130
49131     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
49132         if(!noRefresh){
49133            this.refresh();
49134         }
49135         this.updateHeaderSortState();
49136     },
49137
49138     getScrollState : function(){
49139         
49140         var sb = this.scroller.dom;
49141         return {left: sb.scrollLeft, top: sb.scrollTop};
49142     },
49143
49144     stripeRows : function(startRow){
49145         if(!this.grid.stripeRows || this.ds.getCount() < 1){
49146             return;
49147         }
49148         startRow = startRow || 0;
49149         var rows = this.getBodyTable().rows;
49150         var lrows = this.getLockedTable().rows;
49151         var cls = ' x-grid-row-alt ';
49152         for(var i = startRow, len = rows.length; i < len; i++){
49153             var row = rows[i], lrow = lrows[i];
49154             var isAlt = ((i+1) % 2 == 0);
49155             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
49156             if(isAlt == hasAlt){
49157                 continue;
49158             }
49159             if(isAlt){
49160                 row.className += " x-grid-row-alt";
49161             }else{
49162                 row.className = row.className.replace("x-grid-row-alt", "");
49163             }
49164             if(lrow){
49165                 lrow.className = row.className;
49166             }
49167         }
49168     },
49169
49170     restoreScroll : function(state){
49171         //Roo.log('GridView.restoreScroll');
49172         var sb = this.scroller.dom;
49173         sb.scrollLeft = state.left;
49174         sb.scrollTop = state.top;
49175         this.syncScroll();
49176     },
49177
49178     syncScroll : function(){
49179         //Roo.log('GridView.syncScroll');
49180         var sb = this.scroller.dom;
49181         var sh = this.mainHd.dom;
49182         var bs = this.mainBody.dom;
49183         var lv = this.lockedBody.dom;
49184         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
49185         lv.scrollTop = bs.scrollTop = sb.scrollTop;
49186     },
49187
49188     handleScroll : function(e){
49189         this.syncScroll();
49190         var sb = this.scroller.dom;
49191         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
49192         e.stopEvent();
49193     },
49194
49195     handleWheel : function(e){
49196         var d = e.getWheelDelta();
49197         this.scroller.dom.scrollTop -= d*22;
49198         // set this here to prevent jumpy scrolling on large tables
49199         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
49200         e.stopEvent();
49201     },
49202
49203     renderRows : function(startRow, endRow){
49204         // pull in all the crap needed to render rows
49205         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
49206         var colCount = cm.getColumnCount();
49207
49208         if(ds.getCount() < 1){
49209             return ["", ""];
49210         }
49211
49212         // build a map for all the columns
49213         var cs = [];
49214         for(var i = 0; i < colCount; i++){
49215             var name = cm.getDataIndex(i);
49216             cs[i] = {
49217                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
49218                 renderer : cm.getRenderer(i),
49219                 id : cm.getColumnId(i),
49220                 locked : cm.isLocked(i)
49221             };
49222         }
49223
49224         startRow = startRow || 0;
49225         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
49226
49227         // records to render
49228         var rs = ds.getRange(startRow, endRow);
49229
49230         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
49231     },
49232
49233     // As much as I hate to duplicate code, this was branched because FireFox really hates
49234     // [].join("") on strings. The performance difference was substantial enough to
49235     // branch this function
49236     doRender : Roo.isGecko ?
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                 
49242                 var hasListener = this.grid.hasListener('rowclass');
49243                 var rowcfg = {};
49244                 for(var j = 0, len = rs.length; j < len; j++){
49245                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
49246                     for(var i = 0; i < colCount; i++){
49247                         c = cs[i];
49248                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
49249                         p.id = c.id;
49250                         p.css = p.attr = "";
49251                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
49252                         if(p.value == undefined || p.value === "") p.value = "&#160;";
49253                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
49254                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
49255                         }
49256                         var markup = ct.apply(p);
49257                         if(!c.locked){
49258                             cb+= markup;
49259                         }else{
49260                             lcb+= markup;
49261                         }
49262                     }
49263                     var alt = [];
49264                     if(stripe && ((rowIndex+1) % 2 == 0)){
49265                         alt.push("x-grid-row-alt")
49266                     }
49267                     if(r.dirty){
49268                         alt.push(  " x-grid-dirty-row");
49269                     }
49270                     rp.cells = lcb;
49271                     if(this.getRowClass){
49272                         alt.push(this.getRowClass(r, rowIndex));
49273                     }
49274                     if (hasListener) {
49275                         rowcfg = {
49276                              
49277                             record: r,
49278                             rowIndex : rowIndex,
49279                             rowClass : ''
49280                         }
49281                         this.grid.fireEvent('rowclass', this, rowcfg);
49282                         alt.push(rowcfg.rowClass);
49283                     }
49284                     rp.alt = alt.join(" ");
49285                     lbuf+= rt.apply(rp);
49286                     rp.cells = cb;
49287                     buf+=  rt.apply(rp);
49288                 }
49289                 return [lbuf, buf];
49290             } :
49291             function(cs, rs, ds, startRow, colCount, stripe){
49292                 var ts = this.templates, ct = ts.cell, rt = ts.row;
49293                 // buffers
49294                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
49295                 var hasListener = this.grid.hasListener('rowclass');
49296                 var rowcfg = {};
49297                 for(var j = 0, len = rs.length; j < len; j++){
49298                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
49299                     for(var i = 0; i < colCount; i++){
49300                         c = cs[i];
49301                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
49302                         p.id = c.id;
49303                         p.css = p.attr = "";
49304                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
49305                         if(p.value == undefined || p.value === "") p.value = "&#160;";
49306                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
49307                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
49308                         }
49309                         var markup = ct.apply(p);
49310                         if(!c.locked){
49311                             cb[cb.length] = markup;
49312                         }else{
49313                             lcb[lcb.length] = markup;
49314                         }
49315                     }
49316                     var alt = [];
49317                     if(stripe && ((rowIndex+1) % 2 == 0)){
49318                         alt.push( "x-grid-row-alt");
49319                     }
49320                     if(r.dirty){
49321                         alt.push(" x-grid-dirty-row");
49322                     }
49323                     rp.cells = lcb;
49324                     if(this.getRowClass){
49325                         alt.push( this.getRowClass(r, rowIndex));
49326                     }
49327                     if (hasListener) {
49328                         rowcfg = {
49329                              
49330                             record: r,
49331                             rowIndex : rowIndex,
49332                             rowClass : ''
49333                         }
49334                         this.grid.fireEvent('rowclass', this, rowcfg);
49335                         alt.push(rowcfg.rowClass);
49336                     }
49337                     rp.alt = alt.join(" ");
49338                     rp.cells = lcb.join("");
49339                     lbuf[lbuf.length] = rt.apply(rp);
49340                     rp.cells = cb.join("");
49341                     buf[buf.length] =  rt.apply(rp);
49342                 }
49343                 return [lbuf.join(""), buf.join("")];
49344             },
49345
49346     renderBody : function(){
49347         var markup = this.renderRows();
49348         var bt = this.templates.body;
49349         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
49350     },
49351
49352     /**
49353      * Refreshes the grid
49354      * @param {Boolean} headersToo
49355      */
49356     refresh : function(headersToo){
49357         this.fireEvent("beforerefresh", this);
49358         this.grid.stopEditing();
49359         var result = this.renderBody();
49360         this.lockedBody.update(result[0]);
49361         this.mainBody.update(result[1]);
49362         if(headersToo === true){
49363             this.updateHeaders();
49364             this.updateColumns();
49365             this.updateSplitters();
49366             this.updateHeaderSortState();
49367         }
49368         this.syncRowHeights();
49369         this.layout();
49370         this.fireEvent("refresh", this);
49371     },
49372
49373     handleColumnMove : function(cm, oldIndex, newIndex){
49374         this.indexMap = null;
49375         var s = this.getScrollState();
49376         this.refresh(true);
49377         this.restoreScroll(s);
49378         this.afterMove(newIndex);
49379     },
49380
49381     afterMove : function(colIndex){
49382         if(this.enableMoveAnim && Roo.enableFx){
49383             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
49384         }
49385         // if multisort - fix sortOrder, and reload..
49386         if (this.grid.dataSource.multiSort) {
49387             // the we can call sort again..
49388             var dm = this.grid.dataSource;
49389             var cm = this.grid.colModel;
49390             var so = [];
49391             for(var i = 0; i < cm.config.length; i++ ) {
49392                 
49393                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
49394                     continue; // dont' bother, it's not in sort list or being set.
49395                 }
49396                 
49397                 so.push(cm.config[i].dataIndex);
49398             };
49399             dm.sortOrder = so;
49400             dm.load(dm.lastOptions);
49401             
49402             
49403         }
49404         
49405     },
49406
49407     updateCell : function(dm, rowIndex, dataIndex){
49408         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
49409         if(typeof colIndex == "undefined"){ // not present in grid
49410             return;
49411         }
49412         var cm = this.grid.colModel;
49413         var cell = this.getCell(rowIndex, colIndex);
49414         var cellText = this.getCellText(rowIndex, colIndex);
49415
49416         var p = {
49417             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
49418             id : cm.getColumnId(colIndex),
49419             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
49420         };
49421         var renderer = cm.getRenderer(colIndex);
49422         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
49423         if(typeof val == "undefined" || val === "") val = "&#160;";
49424         cellText.innerHTML = val;
49425         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
49426         this.syncRowHeights(rowIndex, rowIndex);
49427     },
49428
49429     calcColumnWidth : function(colIndex, maxRowsToMeasure){
49430         var maxWidth = 0;
49431         if(this.grid.autoSizeHeaders){
49432             var h = this.getHeaderCellMeasure(colIndex);
49433             maxWidth = Math.max(maxWidth, h.scrollWidth);
49434         }
49435         var tb, index;
49436         if(this.cm.isLocked(colIndex)){
49437             tb = this.getLockedTable();
49438             index = colIndex;
49439         }else{
49440             tb = this.getBodyTable();
49441             index = colIndex - this.cm.getLockedCount();
49442         }
49443         if(tb && tb.rows){
49444             var rows = tb.rows;
49445             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
49446             for(var i = 0; i < stopIndex; i++){
49447                 var cell = rows[i].childNodes[index].firstChild;
49448                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
49449             }
49450         }
49451         return maxWidth + /*margin for error in IE*/ 5;
49452     },
49453     /**
49454      * Autofit a column to its content.
49455      * @param {Number} colIndex
49456      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
49457      */
49458      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
49459          if(this.cm.isHidden(colIndex)){
49460              return; // can't calc a hidden column
49461          }
49462         if(forceMinSize){
49463             var cid = this.cm.getColumnId(colIndex);
49464             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
49465            if(this.grid.autoSizeHeaders){
49466                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
49467            }
49468         }
49469         var newWidth = this.calcColumnWidth(colIndex);
49470         this.cm.setColumnWidth(colIndex,
49471             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
49472         if(!suppressEvent){
49473             this.grid.fireEvent("columnresize", colIndex, newWidth);
49474         }
49475     },
49476
49477     /**
49478      * Autofits all columns to their content and then expands to fit any extra space in the grid
49479      */
49480      autoSizeColumns : function(){
49481         var cm = this.grid.colModel;
49482         var colCount = cm.getColumnCount();
49483         for(var i = 0; i < colCount; i++){
49484             this.autoSizeColumn(i, true, true);
49485         }
49486         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
49487             this.fitColumns();
49488         }else{
49489             this.updateColumns();
49490             this.layout();
49491         }
49492     },
49493
49494     /**
49495      * Autofits all columns to the grid's width proportionate with their current size
49496      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
49497      */
49498     fitColumns : function(reserveScrollSpace){
49499         var cm = this.grid.colModel;
49500         var colCount = cm.getColumnCount();
49501         var cols = [];
49502         var width = 0;
49503         var i, w;
49504         for (i = 0; i < colCount; i++){
49505             if(!cm.isHidden(i) && !cm.isFixed(i)){
49506                 w = cm.getColumnWidth(i);
49507                 cols.push(i);
49508                 cols.push(w);
49509                 width += w;
49510             }
49511         }
49512         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
49513         if(reserveScrollSpace){
49514             avail -= 17;
49515         }
49516         var frac = (avail - cm.getTotalWidth())/width;
49517         while (cols.length){
49518             w = cols.pop();
49519             i = cols.pop();
49520             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
49521         }
49522         this.updateColumns();
49523         this.layout();
49524     },
49525
49526     onRowSelect : function(rowIndex){
49527         var row = this.getRowComposite(rowIndex);
49528         row.addClass("x-grid-row-selected");
49529     },
49530
49531     onRowDeselect : function(rowIndex){
49532         var row = this.getRowComposite(rowIndex);
49533         row.removeClass("x-grid-row-selected");
49534     },
49535
49536     onCellSelect : function(row, col){
49537         var cell = this.getCell(row, col);
49538         if(cell){
49539             Roo.fly(cell).addClass("x-grid-cell-selected");
49540         }
49541     },
49542
49543     onCellDeselect : function(row, col){
49544         var cell = this.getCell(row, col);
49545         if(cell){
49546             Roo.fly(cell).removeClass("x-grid-cell-selected");
49547         }
49548     },
49549
49550     updateHeaderSortState : function(){
49551         
49552         // sort state can be single { field: xxx, direction : yyy}
49553         // or   { xxx=>ASC , yyy : DESC ..... }
49554         
49555         var mstate = {};
49556         if (!this.ds.multiSort) { 
49557             var state = this.ds.getSortState();
49558             if(!state){
49559                 return;
49560             }
49561             mstate[state.field] = state.direction;
49562             // FIXME... - this is not used here.. but might be elsewhere..
49563             this.sortState = state;
49564             
49565         } else {
49566             mstate = this.ds.sortToggle;
49567         }
49568         //remove existing sort classes..
49569         
49570         var sc = this.sortClasses;
49571         var hds = this.el.select(this.headerSelector).removeClass(sc);
49572         
49573         for(var f in mstate) {
49574         
49575             var sortColumn = this.cm.findColumnIndex(f);
49576             
49577             if(sortColumn != -1){
49578                 var sortDir = mstate[f];        
49579                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
49580             }
49581         }
49582         
49583          
49584         
49585     },
49586
49587
49588     handleHeaderClick : function(g, index){
49589         if(this.headersDisabled){
49590             return;
49591         }
49592         var dm = g.dataSource, cm = g.colModel;
49593         if(!cm.isSortable(index)){
49594             return;
49595         }
49596         g.stopEditing();
49597         
49598         if (dm.multiSort) {
49599             // update the sortOrder
49600             var so = [];
49601             for(var i = 0; i < cm.config.length; i++ ) {
49602                 
49603                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
49604                     continue; // dont' bother, it's not in sort list or being set.
49605                 }
49606                 
49607                 so.push(cm.config[i].dataIndex);
49608             };
49609             dm.sortOrder = so;
49610         }
49611         
49612         
49613         dm.sort(cm.getDataIndex(index));
49614     },
49615
49616
49617     destroy : function(){
49618         if(this.colMenu){
49619             this.colMenu.removeAll();
49620             Roo.menu.MenuMgr.unregister(this.colMenu);
49621             this.colMenu.getEl().remove();
49622             delete this.colMenu;
49623         }
49624         if(this.hmenu){
49625             this.hmenu.removeAll();
49626             Roo.menu.MenuMgr.unregister(this.hmenu);
49627             this.hmenu.getEl().remove();
49628             delete this.hmenu;
49629         }
49630         if(this.grid.enableColumnMove){
49631             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
49632             if(dds){
49633                 for(var dd in dds){
49634                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
49635                         var elid = dds[dd].dragElId;
49636                         dds[dd].unreg();
49637                         Roo.get(elid).remove();
49638                     } else if(dds[dd].config.isTarget){
49639                         dds[dd].proxyTop.remove();
49640                         dds[dd].proxyBottom.remove();
49641                         dds[dd].unreg();
49642                     }
49643                     if(Roo.dd.DDM.locationCache[dd]){
49644                         delete Roo.dd.DDM.locationCache[dd];
49645                     }
49646                 }
49647                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
49648             }
49649         }
49650         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
49651         this.bind(null, null);
49652         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
49653     },
49654
49655     handleLockChange : function(){
49656         this.refresh(true);
49657     },
49658
49659     onDenyColumnLock : function(){
49660
49661     },
49662
49663     onDenyColumnHide : function(){
49664
49665     },
49666
49667     handleHdMenuClick : function(item){
49668         var index = this.hdCtxIndex;
49669         var cm = this.cm, ds = this.ds;
49670         switch(item.id){
49671             case "asc":
49672                 ds.sort(cm.getDataIndex(index), "ASC");
49673                 break;
49674             case "desc":
49675                 ds.sort(cm.getDataIndex(index), "DESC");
49676                 break;
49677             case "lock":
49678                 var lc = cm.getLockedCount();
49679                 if(cm.getColumnCount(true) <= lc+1){
49680                     this.onDenyColumnLock();
49681                     return;
49682                 }
49683                 if(lc != index){
49684                     cm.setLocked(index, true, true);
49685                     cm.moveColumn(index, lc);
49686                     this.grid.fireEvent("columnmove", index, lc);
49687                 }else{
49688                     cm.setLocked(index, true);
49689                 }
49690             break;
49691             case "unlock":
49692                 var lc = cm.getLockedCount();
49693                 if((lc-1) != index){
49694                     cm.setLocked(index, false, true);
49695                     cm.moveColumn(index, lc-1);
49696                     this.grid.fireEvent("columnmove", index, lc-1);
49697                 }else{
49698                     cm.setLocked(index, false);
49699                 }
49700             break;
49701             default:
49702                 index = cm.getIndexById(item.id.substr(4));
49703                 if(index != -1){
49704                     if(item.checked && cm.getColumnCount(true) <= 1){
49705                         this.onDenyColumnHide();
49706                         return false;
49707                     }
49708                     cm.setHidden(index, item.checked);
49709                 }
49710         }
49711         return true;
49712     },
49713
49714     beforeColMenuShow : function(){
49715         var cm = this.cm,  colCount = cm.getColumnCount();
49716         this.colMenu.removeAll();
49717         for(var i = 0; i < colCount; i++){
49718             this.colMenu.add(new Roo.menu.CheckItem({
49719                 id: "col-"+cm.getColumnId(i),
49720                 text: cm.getColumnHeader(i),
49721                 checked: !cm.isHidden(i),
49722                 hideOnClick:false
49723             }));
49724         }
49725     },
49726
49727     handleHdCtx : function(g, index, e){
49728         e.stopEvent();
49729         var hd = this.getHeaderCell(index);
49730         this.hdCtxIndex = index;
49731         var ms = this.hmenu.items, cm = this.cm;
49732         ms.get("asc").setDisabled(!cm.isSortable(index));
49733         ms.get("desc").setDisabled(!cm.isSortable(index));
49734         if(this.grid.enableColLock !== false){
49735             ms.get("lock").setDisabled(cm.isLocked(index));
49736             ms.get("unlock").setDisabled(!cm.isLocked(index));
49737         }
49738         this.hmenu.show(hd, "tl-bl");
49739     },
49740
49741     handleHdOver : function(e){
49742         var hd = this.findHeaderCell(e.getTarget());
49743         if(hd && !this.headersDisabled){
49744             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
49745                this.fly(hd).addClass("x-grid-hd-over");
49746             }
49747         }
49748     },
49749
49750     handleHdOut : function(e){
49751         var hd = this.findHeaderCell(e.getTarget());
49752         if(hd){
49753             this.fly(hd).removeClass("x-grid-hd-over");
49754         }
49755     },
49756
49757     handleSplitDblClick : function(e, t){
49758         var i = this.getCellIndex(t);
49759         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
49760             this.autoSizeColumn(i, true);
49761             this.layout();
49762         }
49763     },
49764
49765     render : function(){
49766
49767         var cm = this.cm;
49768         var colCount = cm.getColumnCount();
49769
49770         if(this.grid.monitorWindowResize === true){
49771             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
49772         }
49773         var header = this.renderHeaders();
49774         var body = this.templates.body.apply({rows:""});
49775         var html = this.templates.master.apply({
49776             lockedBody: body,
49777             body: body,
49778             lockedHeader: header[0],
49779             header: header[1]
49780         });
49781
49782         //this.updateColumns();
49783
49784         this.grid.getGridEl().dom.innerHTML = html;
49785
49786         this.initElements();
49787         
49788         // a kludge to fix the random scolling effect in webkit
49789         this.el.on("scroll", function() {
49790             this.el.dom.scrollTop=0; // hopefully not recursive..
49791         },this);
49792
49793         this.scroller.on("scroll", this.handleScroll, this);
49794         this.lockedBody.on("mousewheel", this.handleWheel, this);
49795         this.mainBody.on("mousewheel", this.handleWheel, this);
49796
49797         this.mainHd.on("mouseover", this.handleHdOver, this);
49798         this.mainHd.on("mouseout", this.handleHdOut, this);
49799         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
49800                 {delegate: "."+this.splitClass});
49801
49802         this.lockedHd.on("mouseover", this.handleHdOver, this);
49803         this.lockedHd.on("mouseout", this.handleHdOut, this);
49804         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
49805                 {delegate: "."+this.splitClass});
49806
49807         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
49808             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
49809         }
49810
49811         this.updateSplitters();
49812
49813         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
49814             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
49815             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
49816         }
49817
49818         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
49819             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
49820             this.hmenu.add(
49821                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
49822                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
49823             );
49824             if(this.grid.enableColLock !== false){
49825                 this.hmenu.add('-',
49826                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
49827                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
49828                 );
49829             }
49830             if(this.grid.enableColumnHide !== false){
49831
49832                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
49833                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
49834                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
49835
49836                 this.hmenu.add('-',
49837                     {id:"columns", text: this.columnsText, menu: this.colMenu}
49838                 );
49839             }
49840             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
49841
49842             this.grid.on("headercontextmenu", this.handleHdCtx, this);
49843         }
49844
49845         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
49846             this.dd = new Roo.grid.GridDragZone(this.grid, {
49847                 ddGroup : this.grid.ddGroup || 'GridDD'
49848             });
49849         }
49850
49851         /*
49852         for(var i = 0; i < colCount; i++){
49853             if(cm.isHidden(i)){
49854                 this.hideColumn(i);
49855             }
49856             if(cm.config[i].align){
49857                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
49858                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
49859             }
49860         }*/
49861         
49862         this.updateHeaderSortState();
49863
49864         this.beforeInitialResize();
49865         this.layout(true);
49866
49867         // two part rendering gives faster view to the user
49868         this.renderPhase2.defer(1, this);
49869     },
49870
49871     renderPhase2 : function(){
49872         // render the rows now
49873         this.refresh();
49874         if(this.grid.autoSizeColumns){
49875             this.autoSizeColumns();
49876         }
49877     },
49878
49879     beforeInitialResize : function(){
49880
49881     },
49882
49883     onColumnSplitterMoved : function(i, w){
49884         this.userResized = true;
49885         var cm = this.grid.colModel;
49886         cm.setColumnWidth(i, w, true);
49887         var cid = cm.getColumnId(i);
49888         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
49889         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
49890         this.updateSplitters();
49891         this.layout();
49892         this.grid.fireEvent("columnresize", i, w);
49893     },
49894
49895     syncRowHeights : function(startIndex, endIndex){
49896         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
49897             startIndex = startIndex || 0;
49898             var mrows = this.getBodyTable().rows;
49899             var lrows = this.getLockedTable().rows;
49900             var len = mrows.length-1;
49901             endIndex = Math.min(endIndex || len, len);
49902             for(var i = startIndex; i <= endIndex; i++){
49903                 var m = mrows[i], l = lrows[i];
49904                 var h = Math.max(m.offsetHeight, l.offsetHeight);
49905                 m.style.height = l.style.height = h + "px";
49906             }
49907         }
49908     },
49909
49910     layout : function(initialRender, is2ndPass){
49911         var g = this.grid;
49912         var auto = g.autoHeight;
49913         var scrollOffset = 16;
49914         var c = g.getGridEl(), cm = this.cm,
49915                 expandCol = g.autoExpandColumn,
49916                 gv = this;
49917         //c.beginMeasure();
49918
49919         if(!c.dom.offsetWidth){ // display:none?
49920             if(initialRender){
49921                 this.lockedWrap.show();
49922                 this.mainWrap.show();
49923             }
49924             return;
49925         }
49926
49927         var hasLock = this.cm.isLocked(0);
49928
49929         var tbh = this.headerPanel.getHeight();
49930         var bbh = this.footerPanel.getHeight();
49931
49932         if(auto){
49933             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
49934             var newHeight = ch + c.getBorderWidth("tb");
49935             if(g.maxHeight){
49936                 newHeight = Math.min(g.maxHeight, newHeight);
49937             }
49938             c.setHeight(newHeight);
49939         }
49940
49941         if(g.autoWidth){
49942             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
49943         }
49944
49945         var s = this.scroller;
49946
49947         var csize = c.getSize(true);
49948
49949         this.el.setSize(csize.width, csize.height);
49950
49951         this.headerPanel.setWidth(csize.width);
49952         this.footerPanel.setWidth(csize.width);
49953
49954         var hdHeight = this.mainHd.getHeight();
49955         var vw = csize.width;
49956         var vh = csize.height - (tbh + bbh);
49957
49958         s.setSize(vw, vh);
49959
49960         var bt = this.getBodyTable();
49961         var ltWidth = hasLock ?
49962                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
49963
49964         var scrollHeight = bt.offsetHeight;
49965         var scrollWidth = ltWidth + bt.offsetWidth;
49966         var vscroll = false, hscroll = false;
49967
49968         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
49969
49970         var lw = this.lockedWrap, mw = this.mainWrap;
49971         var lb = this.lockedBody, mb = this.mainBody;
49972
49973         setTimeout(function(){
49974             var t = s.dom.offsetTop;
49975             var w = s.dom.clientWidth,
49976                 h = s.dom.clientHeight;
49977
49978             lw.setTop(t);
49979             lw.setSize(ltWidth, h);
49980
49981             mw.setLeftTop(ltWidth, t);
49982             mw.setSize(w-ltWidth, h);
49983
49984             lb.setHeight(h-hdHeight);
49985             mb.setHeight(h-hdHeight);
49986
49987             if(is2ndPass !== true && !gv.userResized && expandCol){
49988                 // high speed resize without full column calculation
49989                 
49990                 var ci = cm.getIndexById(expandCol);
49991                 if (ci < 0) {
49992                     ci = cm.findColumnIndex(expandCol);
49993                 }
49994                 ci = Math.max(0, ci); // make sure it's got at least the first col.
49995                 var expandId = cm.getColumnId(ci);
49996                 var  tw = cm.getTotalWidth(false);
49997                 var currentWidth = cm.getColumnWidth(ci);
49998                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
49999                 if(currentWidth != cw){
50000                     cm.setColumnWidth(ci, cw, true);
50001                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
50002                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
50003                     gv.updateSplitters();
50004                     gv.layout(false, true);
50005                 }
50006             }
50007
50008             if(initialRender){
50009                 lw.show();
50010                 mw.show();
50011             }
50012             //c.endMeasure();
50013         }, 10);
50014     },
50015
50016     onWindowResize : function(){
50017         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
50018             return;
50019         }
50020         this.layout();
50021     },
50022
50023     appendFooter : function(parentEl){
50024         return null;
50025     },
50026
50027     sortAscText : "Sort Ascending",
50028     sortDescText : "Sort Descending",
50029     lockText : "Lock Column",
50030     unlockText : "Unlock Column",
50031     columnsText : "Columns"
50032 });
50033
50034
50035 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
50036     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
50037     this.proxy.el.addClass('x-grid3-col-dd');
50038 };
50039
50040 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
50041     handleMouseDown : function(e){
50042
50043     },
50044
50045     callHandleMouseDown : function(e){
50046         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
50047     }
50048 });
50049 /*
50050  * Based on:
50051  * Ext JS Library 1.1.1
50052  * Copyright(c) 2006-2007, Ext JS, LLC.
50053  *
50054  * Originally Released Under LGPL - original licence link has changed is not relivant.
50055  *
50056  * Fork - LGPL
50057  * <script type="text/javascript">
50058  */
50059  
50060 // private
50061 // This is a support class used internally by the Grid components
50062 Roo.grid.SplitDragZone = function(grid, hd, hd2){
50063     this.grid = grid;
50064     this.view = grid.getView();
50065     this.proxy = this.view.resizeProxy;
50066     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
50067         "gridSplitters" + this.grid.getGridEl().id, {
50068         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
50069     });
50070     this.setHandleElId(Roo.id(hd));
50071     this.setOuterHandleElId(Roo.id(hd2));
50072     this.scroll = false;
50073 };
50074 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
50075     fly: Roo.Element.fly,
50076
50077     b4StartDrag : function(x, y){
50078         this.view.headersDisabled = true;
50079         this.proxy.setHeight(this.view.mainWrap.getHeight());
50080         var w = this.cm.getColumnWidth(this.cellIndex);
50081         var minw = Math.max(w-this.grid.minColumnWidth, 0);
50082         this.resetConstraints();
50083         this.setXConstraint(minw, 1000);
50084         this.setYConstraint(0, 0);
50085         this.minX = x - minw;
50086         this.maxX = x + 1000;
50087         this.startPos = x;
50088         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
50089     },
50090
50091
50092     handleMouseDown : function(e){
50093         ev = Roo.EventObject.setEvent(e);
50094         var t = this.fly(ev.getTarget());
50095         if(t.hasClass("x-grid-split")){
50096             this.cellIndex = this.view.getCellIndex(t.dom);
50097             this.split = t.dom;
50098             this.cm = this.grid.colModel;
50099             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
50100                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
50101             }
50102         }
50103     },
50104
50105     endDrag : function(e){
50106         this.view.headersDisabled = false;
50107         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
50108         var diff = endX - this.startPos;
50109         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
50110     },
50111
50112     autoOffset : function(){
50113         this.setDelta(0,0);
50114     }
50115 });/*
50116  * Based on:
50117  * Ext JS Library 1.1.1
50118  * Copyright(c) 2006-2007, Ext JS, LLC.
50119  *
50120  * Originally Released Under LGPL - original licence link has changed is not relivant.
50121  *
50122  * Fork - LGPL
50123  * <script type="text/javascript">
50124  */
50125  
50126 // private
50127 // This is a support class used internally by the Grid components
50128 Roo.grid.GridDragZone = function(grid, config){
50129     this.view = grid.getView();
50130     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
50131     if(this.view.lockedBody){
50132         this.setHandleElId(Roo.id(this.view.mainBody.dom));
50133         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
50134     }
50135     this.scroll = false;
50136     this.grid = grid;
50137     this.ddel = document.createElement('div');
50138     this.ddel.className = 'x-grid-dd-wrap';
50139 };
50140
50141 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
50142     ddGroup : "GridDD",
50143
50144     getDragData : function(e){
50145         var t = Roo.lib.Event.getTarget(e);
50146         var rowIndex = this.view.findRowIndex(t);
50147         if(rowIndex !== false){
50148             var sm = this.grid.selModel;
50149             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
50150               //  sm.mouseDown(e, t);
50151             //}
50152             if (e.hasModifier()){
50153                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
50154             }
50155             return {grid: this.grid, ddel: this.ddel, rowIndex: rowIndex, selections:sm.getSelections()};
50156         }
50157         return false;
50158     },
50159
50160     onInitDrag : function(e){
50161         var data = this.dragData;
50162         this.ddel.innerHTML = this.grid.getDragDropText();
50163         this.proxy.update(this.ddel);
50164         // fire start drag?
50165     },
50166
50167     afterRepair : function(){
50168         this.dragging = false;
50169     },
50170
50171     getRepairXY : function(e, data){
50172         return false;
50173     },
50174
50175     onEndDrag : function(data, e){
50176         // fire end drag?
50177     },
50178
50179     onValidDrop : function(dd, e, id){
50180         // fire drag drop?
50181         this.hideProxy();
50182     },
50183
50184     beforeInvalidDrop : function(e, id){
50185
50186     }
50187 });/*
50188  * Based on:
50189  * Ext JS Library 1.1.1
50190  * Copyright(c) 2006-2007, Ext JS, LLC.
50191  *
50192  * Originally Released Under LGPL - original licence link has changed is not relivant.
50193  *
50194  * Fork - LGPL
50195  * <script type="text/javascript">
50196  */
50197  
50198
50199 /**
50200  * @class Roo.grid.ColumnModel
50201  * @extends Roo.util.Observable
50202  * This is the default implementation of a ColumnModel used by the Grid. It defines
50203  * the columns in the grid.
50204  * <br>Usage:<br>
50205  <pre><code>
50206  var colModel = new Roo.grid.ColumnModel([
50207         {header: "Ticker", width: 60, sortable: true, locked: true},
50208         {header: "Company Name", width: 150, sortable: true},
50209         {header: "Market Cap.", width: 100, sortable: true},
50210         {header: "$ Sales", width: 100, sortable: true, renderer: money},
50211         {header: "Employees", width: 100, sortable: true, resizable: false}
50212  ]);
50213  </code></pre>
50214  * <p>
50215  
50216  * The config options listed for this class are options which may appear in each
50217  * individual column definition.
50218  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
50219  * @constructor
50220  * @param {Object} config An Array of column config objects. See this class's
50221  * config objects for details.
50222 */
50223 Roo.grid.ColumnModel = function(config){
50224         /**
50225      * The config passed into the constructor
50226      */
50227     this.config = config;
50228     this.lookup = {};
50229
50230     // if no id, create one
50231     // if the column does not have a dataIndex mapping,
50232     // map it to the order it is in the config
50233     for(var i = 0, len = config.length; i < len; i++){
50234         var c = config[i];
50235         if(typeof c.dataIndex == "undefined"){
50236             c.dataIndex = i;
50237         }
50238         if(typeof c.renderer == "string"){
50239             c.renderer = Roo.util.Format[c.renderer];
50240         }
50241         if(typeof c.id == "undefined"){
50242             c.id = Roo.id();
50243         }
50244         if(c.editor && c.editor.xtype){
50245             c.editor  = Roo.factory(c.editor, Roo.grid);
50246         }
50247         if(c.editor && c.editor.isFormField){
50248             c.editor = new Roo.grid.GridEditor(c.editor);
50249         }
50250         this.lookup[c.id] = c;
50251     }
50252
50253     /**
50254      * The width of columns which have no width specified (defaults to 100)
50255      * @type Number
50256      */
50257     this.defaultWidth = 100;
50258
50259     /**
50260      * Default sortable of columns which have no sortable specified (defaults to false)
50261      * @type Boolean
50262      */
50263     this.defaultSortable = false;
50264
50265     this.addEvents({
50266         /**
50267              * @event widthchange
50268              * Fires when the width of a column changes.
50269              * @param {ColumnModel} this
50270              * @param {Number} columnIndex The column index
50271              * @param {Number} newWidth The new width
50272              */
50273             "widthchange": true,
50274         /**
50275              * @event headerchange
50276              * Fires when the text of a header changes.
50277              * @param {ColumnModel} this
50278              * @param {Number} columnIndex The column index
50279              * @param {Number} newText The new header text
50280              */
50281             "headerchange": true,
50282         /**
50283              * @event hiddenchange
50284              * Fires when a column is hidden or "unhidden".
50285              * @param {ColumnModel} this
50286              * @param {Number} columnIndex The column index
50287              * @param {Boolean} hidden true if hidden, false otherwise
50288              */
50289             "hiddenchange": true,
50290             /**
50291          * @event columnmoved
50292          * Fires when a column is moved.
50293          * @param {ColumnModel} this
50294          * @param {Number} oldIndex
50295          * @param {Number} newIndex
50296          */
50297         "columnmoved" : true,
50298         /**
50299          * @event columlockchange
50300          * Fires when a column's locked state is changed
50301          * @param {ColumnModel} this
50302          * @param {Number} colIndex
50303          * @param {Boolean} locked true if locked
50304          */
50305         "columnlockchange" : true
50306     });
50307     Roo.grid.ColumnModel.superclass.constructor.call(this);
50308 };
50309 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
50310     /**
50311      * @cfg {String} header The header text to display in the Grid view.
50312      */
50313     /**
50314      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
50315      * {@link Roo.data.Record} definition from which to draw the column's value. If not
50316      * specified, the column's index is used as an index into the Record's data Array.
50317      */
50318     /**
50319      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
50320      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
50321      */
50322     /**
50323      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
50324      * Defaults to the value of the {@link #defaultSortable} property.
50325      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
50326      */
50327     /**
50328      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
50329      */
50330     /**
50331      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
50332      */
50333     /**
50334      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
50335      */
50336     /**
50337      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
50338      */
50339     /**
50340      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
50341      * given the cell's data value. See {@link #setRenderer}. If not specified, the
50342      * default renderer uses the raw data value.
50343      */
50344        /**
50345      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
50346      */
50347     /**
50348      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
50349      */
50350
50351     /**
50352      * Returns the id of the column at the specified index.
50353      * @param {Number} index The column index
50354      * @return {String} the id
50355      */
50356     getColumnId : function(index){
50357         return this.config[index].id;
50358     },
50359
50360     /**
50361      * Returns the column for a specified id.
50362      * @param {String} id The column id
50363      * @return {Object} the column
50364      */
50365     getColumnById : function(id){
50366         return this.lookup[id];
50367     },
50368
50369     
50370     /**
50371      * Returns the column for a specified dataIndex.
50372      * @param {String} dataIndex The column dataIndex
50373      * @return {Object|Boolean} the column or false if not found
50374      */
50375     getColumnByDataIndex: function(dataIndex){
50376         var index = this.findColumnIndex(dataIndex);
50377         return index > -1 ? this.config[index] : false;
50378     },
50379     
50380     /**
50381      * Returns the index for a specified column id.
50382      * @param {String} id The column id
50383      * @return {Number} the index, or -1 if not found
50384      */
50385     getIndexById : function(id){
50386         for(var i = 0, len = this.config.length; i < len; i++){
50387             if(this.config[i].id == id){
50388                 return i;
50389             }
50390         }
50391         return -1;
50392     },
50393     
50394     /**
50395      * Returns the index for a specified column dataIndex.
50396      * @param {String} dataIndex The column dataIndex
50397      * @return {Number} the index, or -1 if not found
50398      */
50399     
50400     findColumnIndex : function(dataIndex){
50401         for(var i = 0, len = this.config.length; i < len; i++){
50402             if(this.config[i].dataIndex == dataIndex){
50403                 return i;
50404             }
50405         }
50406         return -1;
50407     },
50408     
50409     
50410     moveColumn : function(oldIndex, newIndex){
50411         var c = this.config[oldIndex];
50412         this.config.splice(oldIndex, 1);
50413         this.config.splice(newIndex, 0, c);
50414         this.dataMap = null;
50415         this.fireEvent("columnmoved", this, oldIndex, newIndex);
50416     },
50417
50418     isLocked : function(colIndex){
50419         return this.config[colIndex].locked === true;
50420     },
50421
50422     setLocked : function(colIndex, value, suppressEvent){
50423         if(this.isLocked(colIndex) == value){
50424             return;
50425         }
50426         this.config[colIndex].locked = value;
50427         if(!suppressEvent){
50428             this.fireEvent("columnlockchange", this, colIndex, value);
50429         }
50430     },
50431
50432     getTotalLockedWidth : function(){
50433         var totalWidth = 0;
50434         for(var i = 0; i < this.config.length; i++){
50435             if(this.isLocked(i) && !this.isHidden(i)){
50436                 this.totalWidth += this.getColumnWidth(i);
50437             }
50438         }
50439         return totalWidth;
50440     },
50441
50442     getLockedCount : function(){
50443         for(var i = 0, len = this.config.length; i < len; i++){
50444             if(!this.isLocked(i)){
50445                 return i;
50446             }
50447         }
50448     },
50449
50450     /**
50451      * Returns the number of columns.
50452      * @return {Number}
50453      */
50454     getColumnCount : function(visibleOnly){
50455         if(visibleOnly === true){
50456             var c = 0;
50457             for(var i = 0, len = this.config.length; i < len; i++){
50458                 if(!this.isHidden(i)){
50459                     c++;
50460                 }
50461             }
50462             return c;
50463         }
50464         return this.config.length;
50465     },
50466
50467     /**
50468      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
50469      * @param {Function} fn
50470      * @param {Object} scope (optional)
50471      * @return {Array} result
50472      */
50473     getColumnsBy : function(fn, scope){
50474         var r = [];
50475         for(var i = 0, len = this.config.length; i < len; i++){
50476             var c = this.config[i];
50477             if(fn.call(scope||this, c, i) === true){
50478                 r[r.length] = c;
50479             }
50480         }
50481         return r;
50482     },
50483
50484     /**
50485      * Returns true if the specified column is sortable.
50486      * @param {Number} col The column index
50487      * @return {Boolean}
50488      */
50489     isSortable : function(col){
50490         if(typeof this.config[col].sortable == "undefined"){
50491             return this.defaultSortable;
50492         }
50493         return this.config[col].sortable;
50494     },
50495
50496     /**
50497      * Returns the rendering (formatting) function defined for the column.
50498      * @param {Number} col The column index.
50499      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
50500      */
50501     getRenderer : function(col){
50502         if(!this.config[col].renderer){
50503             return Roo.grid.ColumnModel.defaultRenderer;
50504         }
50505         return this.config[col].renderer;
50506     },
50507
50508     /**
50509      * Sets the rendering (formatting) function for a column.
50510      * @param {Number} col The column index
50511      * @param {Function} fn The function to use to process the cell's raw data
50512      * to return HTML markup for the grid view. The render function is called with
50513      * the following parameters:<ul>
50514      * <li>Data value.</li>
50515      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
50516      * <li>css A CSS style string to apply to the table cell.</li>
50517      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
50518      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
50519      * <li>Row index</li>
50520      * <li>Column index</li>
50521      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
50522      */
50523     setRenderer : function(col, fn){
50524         this.config[col].renderer = fn;
50525     },
50526
50527     /**
50528      * Returns the width for the specified column.
50529      * @param {Number} col The column index
50530      * @return {Number}
50531      */
50532     getColumnWidth : function(col){
50533         return this.config[col].width * 1 || this.defaultWidth;
50534     },
50535
50536     /**
50537      * Sets the width for a column.
50538      * @param {Number} col The column index
50539      * @param {Number} width The new width
50540      */
50541     setColumnWidth : function(col, width, suppressEvent){
50542         this.config[col].width = width;
50543         this.totalWidth = null;
50544         if(!suppressEvent){
50545              this.fireEvent("widthchange", this, col, width);
50546         }
50547     },
50548
50549     /**
50550      * Returns the total width of all columns.
50551      * @param {Boolean} includeHidden True to include hidden column widths
50552      * @return {Number}
50553      */
50554     getTotalWidth : function(includeHidden){
50555         if(!this.totalWidth){
50556             this.totalWidth = 0;
50557             for(var i = 0, len = this.config.length; i < len; i++){
50558                 if(includeHidden || !this.isHidden(i)){
50559                     this.totalWidth += this.getColumnWidth(i);
50560                 }
50561             }
50562         }
50563         return this.totalWidth;
50564     },
50565
50566     /**
50567      * Returns the header for the specified column.
50568      * @param {Number} col The column index
50569      * @return {String}
50570      */
50571     getColumnHeader : function(col){
50572         return this.config[col].header;
50573     },
50574
50575     /**
50576      * Sets the header for a column.
50577      * @param {Number} col The column index
50578      * @param {String} header The new header
50579      */
50580     setColumnHeader : function(col, header){
50581         this.config[col].header = header;
50582         this.fireEvent("headerchange", this, col, header);
50583     },
50584
50585     /**
50586      * Returns the tooltip for the specified column.
50587      * @param {Number} col The column index
50588      * @return {String}
50589      */
50590     getColumnTooltip : function(col){
50591             return this.config[col].tooltip;
50592     },
50593     /**
50594      * Sets the tooltip for a column.
50595      * @param {Number} col The column index
50596      * @param {String} tooltip The new tooltip
50597      */
50598     setColumnTooltip : function(col, tooltip){
50599             this.config[col].tooltip = tooltip;
50600     },
50601
50602     /**
50603      * Returns the dataIndex for the specified column.
50604      * @param {Number} col The column index
50605      * @return {Number}
50606      */
50607     getDataIndex : function(col){
50608         return this.config[col].dataIndex;
50609     },
50610
50611     /**
50612      * Sets the dataIndex for a column.
50613      * @param {Number} col The column index
50614      * @param {Number} dataIndex The new dataIndex
50615      */
50616     setDataIndex : function(col, dataIndex){
50617         this.config[col].dataIndex = dataIndex;
50618     },
50619
50620     
50621     
50622     /**
50623      * Returns true if the cell is editable.
50624      * @param {Number} colIndex The column index
50625      * @param {Number} rowIndex The row index
50626      * @return {Boolean}
50627      */
50628     isCellEditable : function(colIndex, rowIndex){
50629         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
50630     },
50631
50632     /**
50633      * Returns the editor defined for the cell/column.
50634      * return false or null to disable editing.
50635      * @param {Number} colIndex The column index
50636      * @param {Number} rowIndex The row index
50637      * @return {Object}
50638      */
50639     getCellEditor : function(colIndex, rowIndex){
50640         return this.config[colIndex].editor;
50641     },
50642
50643     /**
50644      * Sets if a column is editable.
50645      * @param {Number} col The column index
50646      * @param {Boolean} editable True if the column is editable
50647      */
50648     setEditable : function(col, editable){
50649         this.config[col].editable = editable;
50650     },
50651
50652
50653     /**
50654      * Returns true if the column is hidden.
50655      * @param {Number} colIndex The column index
50656      * @return {Boolean}
50657      */
50658     isHidden : function(colIndex){
50659         return this.config[colIndex].hidden;
50660     },
50661
50662
50663     /**
50664      * Returns true if the column width cannot be changed
50665      */
50666     isFixed : function(colIndex){
50667         return this.config[colIndex].fixed;
50668     },
50669
50670     /**
50671      * Returns true if the column can be resized
50672      * @return {Boolean}
50673      */
50674     isResizable : function(colIndex){
50675         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
50676     },
50677     /**
50678      * Sets if a column is hidden.
50679      * @param {Number} colIndex The column index
50680      * @param {Boolean} hidden True if the column is hidden
50681      */
50682     setHidden : function(colIndex, hidden){
50683         this.config[colIndex].hidden = hidden;
50684         this.totalWidth = null;
50685         this.fireEvent("hiddenchange", this, colIndex, hidden);
50686     },
50687
50688     /**
50689      * Sets the editor for a column.
50690      * @param {Number} col The column index
50691      * @param {Object} editor The editor object
50692      */
50693     setEditor : function(col, editor){
50694         this.config[col].editor = editor;
50695     }
50696 });
50697
50698 Roo.grid.ColumnModel.defaultRenderer = function(value){
50699         if(typeof value == "string" && value.length < 1){
50700             return "&#160;";
50701         }
50702         return value;
50703 };
50704
50705 // Alias for backwards compatibility
50706 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
50707 /*
50708  * Based on:
50709  * Ext JS Library 1.1.1
50710  * Copyright(c) 2006-2007, Ext JS, LLC.
50711  *
50712  * Originally Released Under LGPL - original licence link has changed is not relivant.
50713  *
50714  * Fork - LGPL
50715  * <script type="text/javascript">
50716  */
50717
50718 /**
50719  * @class Roo.grid.AbstractSelectionModel
50720  * @extends Roo.util.Observable
50721  * Abstract base class for grid SelectionModels.  It provides the interface that should be
50722  * implemented by descendant classes.  This class should not be directly instantiated.
50723  * @constructor
50724  */
50725 Roo.grid.AbstractSelectionModel = function(){
50726     this.locked = false;
50727     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
50728 };
50729
50730 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
50731     /** @ignore Called by the grid automatically. Do not call directly. */
50732     init : function(grid){
50733         this.grid = grid;
50734         this.initEvents();
50735     },
50736
50737     /**
50738      * Locks the selections.
50739      */
50740     lock : function(){
50741         this.locked = true;
50742     },
50743
50744     /**
50745      * Unlocks the selections.
50746      */
50747     unlock : function(){
50748         this.locked = false;
50749     },
50750
50751     /**
50752      * Returns true if the selections are locked.
50753      * @return {Boolean}
50754      */
50755     isLocked : function(){
50756         return this.locked;
50757     }
50758 });/*
50759  * Based on:
50760  * Ext JS Library 1.1.1
50761  * Copyright(c) 2006-2007, Ext JS, LLC.
50762  *
50763  * Originally Released Under LGPL - original licence link has changed is not relivant.
50764  *
50765  * Fork - LGPL
50766  * <script type="text/javascript">
50767  */
50768 /**
50769  * @extends Roo.grid.AbstractSelectionModel
50770  * @class Roo.grid.RowSelectionModel
50771  * The default SelectionModel used by {@link Roo.grid.Grid}.
50772  * It supports multiple selections and keyboard selection/navigation. 
50773  * @constructor
50774  * @param {Object} config
50775  */
50776 Roo.grid.RowSelectionModel = function(config){
50777     Roo.apply(this, config);
50778     this.selections = new Roo.util.MixedCollection(false, function(o){
50779         return o.id;
50780     });
50781
50782     this.last = false;
50783     this.lastActive = false;
50784
50785     this.addEvents({
50786         /**
50787              * @event selectionchange
50788              * Fires when the selection changes
50789              * @param {SelectionModel} this
50790              */
50791             "selectionchange" : true,
50792         /**
50793              * @event afterselectionchange
50794              * Fires after the selection changes (eg. by key press or clicking)
50795              * @param {SelectionModel} this
50796              */
50797             "afterselectionchange" : true,
50798         /**
50799              * @event beforerowselect
50800              * Fires when a row is selected being selected, return false to cancel.
50801              * @param {SelectionModel} this
50802              * @param {Number} rowIndex The selected index
50803              * @param {Boolean} keepExisting False if other selections will be cleared
50804              */
50805             "beforerowselect" : true,
50806         /**
50807              * @event rowselect
50808              * Fires when a row is selected.
50809              * @param {SelectionModel} this
50810              * @param {Number} rowIndex The selected index
50811              * @param {Roo.data.Record} r The record
50812              */
50813             "rowselect" : true,
50814         /**
50815              * @event rowdeselect
50816              * Fires when a row is deselected.
50817              * @param {SelectionModel} this
50818              * @param {Number} rowIndex The selected index
50819              */
50820         "rowdeselect" : true
50821     });
50822     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
50823     this.locked = false;
50824 };
50825
50826 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
50827     /**
50828      * @cfg {Boolean} singleSelect
50829      * True to allow selection of only one row at a time (defaults to false)
50830      */
50831     singleSelect : false,
50832
50833     // private
50834     initEvents : function(){
50835
50836         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
50837             this.grid.on("mousedown", this.handleMouseDown, this);
50838         }else{ // allow click to work like normal
50839             this.grid.on("rowclick", this.handleDragableRowClick, this);
50840         }
50841
50842         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
50843             "up" : function(e){
50844                 if(!e.shiftKey){
50845                     this.selectPrevious(e.shiftKey);
50846                 }else if(this.last !== false && this.lastActive !== false){
50847                     var last = this.last;
50848                     this.selectRange(this.last,  this.lastActive-1);
50849                     this.grid.getView().focusRow(this.lastActive);
50850                     if(last !== false){
50851                         this.last = last;
50852                     }
50853                 }else{
50854                     this.selectFirstRow();
50855                 }
50856                 this.fireEvent("afterselectionchange", this);
50857             },
50858             "down" : function(e){
50859                 if(!e.shiftKey){
50860                     this.selectNext(e.shiftKey);
50861                 }else if(this.last !== false && this.lastActive !== false){
50862                     var last = this.last;
50863                     this.selectRange(this.last,  this.lastActive+1);
50864                     this.grid.getView().focusRow(this.lastActive);
50865                     if(last !== false){
50866                         this.last = last;
50867                     }
50868                 }else{
50869                     this.selectFirstRow();
50870                 }
50871                 this.fireEvent("afterselectionchange", this);
50872             },
50873             scope: this
50874         });
50875
50876         var view = this.grid.view;
50877         view.on("refresh", this.onRefresh, this);
50878         view.on("rowupdated", this.onRowUpdated, this);
50879         view.on("rowremoved", this.onRemove, this);
50880     },
50881
50882     // private
50883     onRefresh : function(){
50884         var ds = this.grid.dataSource, i, v = this.grid.view;
50885         var s = this.selections;
50886         s.each(function(r){
50887             if((i = ds.indexOfId(r.id)) != -1){
50888                 v.onRowSelect(i);
50889             }else{
50890                 s.remove(r);
50891             }
50892         });
50893     },
50894
50895     // private
50896     onRemove : function(v, index, r){
50897         this.selections.remove(r);
50898     },
50899
50900     // private
50901     onRowUpdated : function(v, index, r){
50902         if(this.isSelected(r)){
50903             v.onRowSelect(index);
50904         }
50905     },
50906
50907     /**
50908      * Select records.
50909      * @param {Array} records The records to select
50910      * @param {Boolean} keepExisting (optional) True to keep existing selections
50911      */
50912     selectRecords : function(records, keepExisting){
50913         if(!keepExisting){
50914             this.clearSelections();
50915         }
50916         var ds = this.grid.dataSource;
50917         for(var i = 0, len = records.length; i < len; i++){
50918             this.selectRow(ds.indexOf(records[i]), true);
50919         }
50920     },
50921
50922     /**
50923      * Gets the number of selected rows.
50924      * @return {Number}
50925      */
50926     getCount : function(){
50927         return this.selections.length;
50928     },
50929
50930     /**
50931      * Selects the first row in the grid.
50932      */
50933     selectFirstRow : function(){
50934         this.selectRow(0);
50935     },
50936
50937     /**
50938      * Select the last row.
50939      * @param {Boolean} keepExisting (optional) True to keep existing selections
50940      */
50941     selectLastRow : function(keepExisting){
50942         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
50943     },
50944
50945     /**
50946      * Selects the row immediately following the last selected row.
50947      * @param {Boolean} keepExisting (optional) True to keep existing selections
50948      */
50949     selectNext : function(keepExisting){
50950         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
50951             this.selectRow(this.last+1, keepExisting);
50952             this.grid.getView().focusRow(this.last);
50953         }
50954     },
50955
50956     /**
50957      * Selects the row that precedes the last selected row.
50958      * @param {Boolean} keepExisting (optional) True to keep existing selections
50959      */
50960     selectPrevious : function(keepExisting){
50961         if(this.last){
50962             this.selectRow(this.last-1, keepExisting);
50963             this.grid.getView().focusRow(this.last);
50964         }
50965     },
50966
50967     /**
50968      * Returns the selected records
50969      * @return {Array} Array of selected records
50970      */
50971     getSelections : function(){
50972         return [].concat(this.selections.items);
50973     },
50974
50975     /**
50976      * Returns the first selected record.
50977      * @return {Record}
50978      */
50979     getSelected : function(){
50980         return this.selections.itemAt(0);
50981     },
50982
50983
50984     /**
50985      * Clears all selections.
50986      */
50987     clearSelections : function(fast){
50988         if(this.locked) return;
50989         if(fast !== true){
50990             var ds = this.grid.dataSource;
50991             var s = this.selections;
50992             s.each(function(r){
50993                 this.deselectRow(ds.indexOfId(r.id));
50994             }, this);
50995             s.clear();
50996         }else{
50997             this.selections.clear();
50998         }
50999         this.last = false;
51000     },
51001
51002
51003     /**
51004      * Selects all rows.
51005      */
51006     selectAll : function(){
51007         if(this.locked) return;
51008         this.selections.clear();
51009         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
51010             this.selectRow(i, true);
51011         }
51012     },
51013
51014     /**
51015      * Returns True if there is a selection.
51016      * @return {Boolean}
51017      */
51018     hasSelection : function(){
51019         return this.selections.length > 0;
51020     },
51021
51022     /**
51023      * Returns True if the specified row is selected.
51024      * @param {Number/Record} record The record or index of the record to check
51025      * @return {Boolean}
51026      */
51027     isSelected : function(index){
51028         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
51029         return (r && this.selections.key(r.id) ? true : false);
51030     },
51031
51032     /**
51033      * Returns True if the specified record id is selected.
51034      * @param {String} id The id of record to check
51035      * @return {Boolean}
51036      */
51037     isIdSelected : function(id){
51038         return (this.selections.key(id) ? true : false);
51039     },
51040
51041     // private
51042     handleMouseDown : function(e, t){
51043         var view = this.grid.getView(), rowIndex;
51044         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
51045             return;
51046         };
51047         if(e.shiftKey && this.last !== false){
51048             var last = this.last;
51049             this.selectRange(last, rowIndex, e.ctrlKey);
51050             this.last = last; // reset the last
51051             view.focusRow(rowIndex);
51052         }else{
51053             var isSelected = this.isSelected(rowIndex);
51054             if(e.button !== 0 && isSelected){
51055                 view.focusRow(rowIndex);
51056             }else if(e.ctrlKey && isSelected){
51057                 this.deselectRow(rowIndex);
51058             }else if(!isSelected){
51059                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
51060                 view.focusRow(rowIndex);
51061             }
51062         }
51063         this.fireEvent("afterselectionchange", this);
51064     },
51065     // private
51066     handleDragableRowClick :  function(grid, rowIndex, e) 
51067     {
51068         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
51069             this.selectRow(rowIndex, false);
51070             grid.view.focusRow(rowIndex);
51071              this.fireEvent("afterselectionchange", this);
51072         }
51073     },
51074     
51075     /**
51076      * Selects multiple rows.
51077      * @param {Array} rows Array of the indexes of the row to select
51078      * @param {Boolean} keepExisting (optional) True to keep existing selections
51079      */
51080     selectRows : function(rows, keepExisting){
51081         if(!keepExisting){
51082             this.clearSelections();
51083         }
51084         for(var i = 0, len = rows.length; i < len; i++){
51085             this.selectRow(rows[i], true);
51086         }
51087     },
51088
51089     /**
51090      * Selects a range of rows. All rows in between startRow and endRow are also selected.
51091      * @param {Number} startRow The index of the first row in the range
51092      * @param {Number} endRow The index of the last row in the range
51093      * @param {Boolean} keepExisting (optional) True to retain existing selections
51094      */
51095     selectRange : function(startRow, endRow, keepExisting){
51096         if(this.locked) return;
51097         if(!keepExisting){
51098             this.clearSelections();
51099         }
51100         if(startRow <= endRow){
51101             for(var i = startRow; i <= endRow; i++){
51102                 this.selectRow(i, true);
51103             }
51104         }else{
51105             for(var i = startRow; i >= endRow; i--){
51106                 this.selectRow(i, true);
51107             }
51108         }
51109     },
51110
51111     /**
51112      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
51113      * @param {Number} startRow The index of the first row in the range
51114      * @param {Number} endRow The index of the last row in the range
51115      */
51116     deselectRange : function(startRow, endRow, preventViewNotify){
51117         if(this.locked) return;
51118         for(var i = startRow; i <= endRow; i++){
51119             this.deselectRow(i, preventViewNotify);
51120         }
51121     },
51122
51123     /**
51124      * Selects a row.
51125      * @param {Number} row The index of the row to select
51126      * @param {Boolean} keepExisting (optional) True to keep existing selections
51127      */
51128     selectRow : function(index, keepExisting, preventViewNotify){
51129         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
51130         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
51131             if(!keepExisting || this.singleSelect){
51132                 this.clearSelections();
51133             }
51134             var r = this.grid.dataSource.getAt(index);
51135             this.selections.add(r);
51136             this.last = this.lastActive = index;
51137             if(!preventViewNotify){
51138                 this.grid.getView().onRowSelect(index);
51139             }
51140             this.fireEvent("rowselect", this, index, r);
51141             this.fireEvent("selectionchange", this);
51142         }
51143     },
51144
51145     /**
51146      * Deselects a row.
51147      * @param {Number} row The index of the row to deselect
51148      */
51149     deselectRow : function(index, preventViewNotify){
51150         if(this.locked) return;
51151         if(this.last == index){
51152             this.last = false;
51153         }
51154         if(this.lastActive == index){
51155             this.lastActive = false;
51156         }
51157         var r = this.grid.dataSource.getAt(index);
51158         this.selections.remove(r);
51159         if(!preventViewNotify){
51160             this.grid.getView().onRowDeselect(index);
51161         }
51162         this.fireEvent("rowdeselect", this, index);
51163         this.fireEvent("selectionchange", this);
51164     },
51165
51166     // private
51167     restoreLast : function(){
51168         if(this._last){
51169             this.last = this._last;
51170         }
51171     },
51172
51173     // private
51174     acceptsNav : function(row, col, cm){
51175         return !cm.isHidden(col) && cm.isCellEditable(col, row);
51176     },
51177
51178     // private
51179     onEditorKey : function(field, e){
51180         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
51181         if(k == e.TAB){
51182             e.stopEvent();
51183             ed.completeEdit();
51184             if(e.shiftKey){
51185                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
51186             }else{
51187                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
51188             }
51189         }else if(k == e.ENTER && !e.ctrlKey){
51190             e.stopEvent();
51191             ed.completeEdit();
51192             if(e.shiftKey){
51193                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
51194             }else{
51195                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
51196             }
51197         }else if(k == e.ESC){
51198             ed.cancelEdit();
51199         }
51200         if(newCell){
51201             g.startEditing(newCell[0], newCell[1]);
51202         }
51203     }
51204 });/*
51205  * Based on:
51206  * Ext JS Library 1.1.1
51207  * Copyright(c) 2006-2007, Ext JS, LLC.
51208  *
51209  * Originally Released Under LGPL - original licence link has changed is not relivant.
51210  *
51211  * Fork - LGPL
51212  * <script type="text/javascript">
51213  */
51214 /**
51215  * @class Roo.grid.CellSelectionModel
51216  * @extends Roo.grid.AbstractSelectionModel
51217  * This class provides the basic implementation for cell selection in a grid.
51218  * @constructor
51219  * @param {Object} config The object containing the configuration of this model.
51220  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
51221  */
51222 Roo.grid.CellSelectionModel = function(config){
51223     Roo.apply(this, config);
51224
51225     this.selection = null;
51226
51227     this.addEvents({
51228         /**
51229              * @event beforerowselect
51230              * Fires before a cell is selected.
51231              * @param {SelectionModel} this
51232              * @param {Number} rowIndex The selected row index
51233              * @param {Number} colIndex The selected cell index
51234              */
51235             "beforecellselect" : true,
51236         /**
51237              * @event cellselect
51238              * Fires when a cell is selected.
51239              * @param {SelectionModel} this
51240              * @param {Number} rowIndex The selected row index
51241              * @param {Number} colIndex The selected cell index
51242              */
51243             "cellselect" : true,
51244         /**
51245              * @event selectionchange
51246              * Fires when the active selection changes.
51247              * @param {SelectionModel} this
51248              * @param {Object} selection null for no selection or an object (o) with two properties
51249                 <ul>
51250                 <li>o.record: the record object for the row the selection is in</li>
51251                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
51252                 </ul>
51253              */
51254             "selectionchange" : true,
51255         /**
51256              * @event tabend
51257              * Fires when the tab (or enter) was pressed on the last editable cell
51258              * You can use this to trigger add new row.
51259              * @param {SelectionModel} this
51260              */
51261             "tabend" : true
51262     });
51263     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
51264 };
51265
51266 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
51267     
51268     enter_is_tab: false,
51269
51270     /** @ignore */
51271     initEvents : function(){
51272         this.grid.on("mousedown", this.handleMouseDown, this);
51273         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
51274         var view = this.grid.view;
51275         view.on("refresh", this.onViewChange, this);
51276         view.on("rowupdated", this.onRowUpdated, this);
51277         view.on("beforerowremoved", this.clearSelections, this);
51278         view.on("beforerowsinserted", this.clearSelections, this);
51279         if(this.grid.isEditor){
51280             this.grid.on("beforeedit", this.beforeEdit,  this);
51281         }
51282     },
51283
51284         //private
51285     beforeEdit : function(e){
51286         this.select(e.row, e.column, false, true, e.record);
51287     },
51288
51289         //private
51290     onRowUpdated : function(v, index, r){
51291         if(this.selection && this.selection.record == r){
51292             v.onCellSelect(index, this.selection.cell[1]);
51293         }
51294     },
51295
51296         //private
51297     onViewChange : function(){
51298         this.clearSelections(true);
51299     },
51300
51301         /**
51302          * Returns the currently selected cell,.
51303          * @return {Array} The selected cell (row, column) or null if none selected.
51304          */
51305     getSelectedCell : function(){
51306         return this.selection ? this.selection.cell : null;
51307     },
51308
51309     /**
51310      * Clears all selections.
51311      * @param {Boolean} true to prevent the gridview from being notified about the change.
51312      */
51313     clearSelections : function(preventNotify){
51314         var s = this.selection;
51315         if(s){
51316             if(preventNotify !== true){
51317                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
51318             }
51319             this.selection = null;
51320             this.fireEvent("selectionchange", this, null);
51321         }
51322     },
51323
51324     /**
51325      * Returns true if there is a selection.
51326      * @return {Boolean}
51327      */
51328     hasSelection : function(){
51329         return this.selection ? true : false;
51330     },
51331
51332     /** @ignore */
51333     handleMouseDown : function(e, t){
51334         var v = this.grid.getView();
51335         if(this.isLocked()){
51336             return;
51337         };
51338         var row = v.findRowIndex(t);
51339         var cell = v.findCellIndex(t);
51340         if(row !== false && cell !== false){
51341             this.select(row, cell);
51342         }
51343     },
51344
51345     /**
51346      * Selects a cell.
51347      * @param {Number} rowIndex
51348      * @param {Number} collIndex
51349      */
51350     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
51351         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
51352             this.clearSelections();
51353             r = r || this.grid.dataSource.getAt(rowIndex);
51354             this.selection = {
51355                 record : r,
51356                 cell : [rowIndex, colIndex]
51357             };
51358             if(!preventViewNotify){
51359                 var v = this.grid.getView();
51360                 v.onCellSelect(rowIndex, colIndex);
51361                 if(preventFocus !== true){
51362                     v.focusCell(rowIndex, colIndex);
51363                 }
51364             }
51365             this.fireEvent("cellselect", this, rowIndex, colIndex);
51366             this.fireEvent("selectionchange", this, this.selection);
51367         }
51368     },
51369
51370         //private
51371     isSelectable : function(rowIndex, colIndex, cm){
51372         return !cm.isHidden(colIndex);
51373     },
51374
51375     /** @ignore */
51376     handleKeyDown : function(e){
51377         //Roo.log('Cell Sel Model handleKeyDown');
51378         if(!e.isNavKeyPress()){
51379             return;
51380         }
51381         var g = this.grid, s = this.selection;
51382         if(!s){
51383             e.stopEvent();
51384             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
51385             if(cell){
51386                 this.select(cell[0], cell[1]);
51387             }
51388             return;
51389         }
51390         var sm = this;
51391         var walk = function(row, col, step){
51392             return g.walkCells(row, col, step, sm.isSelectable,  sm);
51393         };
51394         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
51395         var newCell;
51396
51397       
51398
51399         switch(k){
51400             case e.TAB:
51401                 // handled by onEditorKey
51402                 if (g.isEditor && g.editing) {
51403                     return;
51404                 }
51405                 if(e.shiftKey) {
51406                     newCell = walk(r, c-1, -1);
51407                 } else {
51408                     newCell = walk(r, c+1, 1);
51409                 }
51410                 break;
51411             
51412             case e.DOWN:
51413                newCell = walk(r+1, c, 1);
51414                 break;
51415             
51416             case e.UP:
51417                 newCell = walk(r-1, c, -1);
51418                 break;
51419             
51420             case e.RIGHT:
51421                 newCell = walk(r, c+1, 1);
51422                 break;
51423             
51424             case e.LEFT:
51425                 newCell = walk(r, c-1, -1);
51426                 break;
51427             
51428             case e.ENTER:
51429                 
51430                 if(g.isEditor && !g.editing){
51431                    g.startEditing(r, c);
51432                    e.stopEvent();
51433                    return;
51434                 }
51435                 
51436                 
51437              break;
51438         };
51439         if(newCell){
51440             this.select(newCell[0], newCell[1]);
51441             e.stopEvent();
51442             
51443         }
51444     },
51445
51446     acceptsNav : function(row, col, cm){
51447         return !cm.isHidden(col) && cm.isCellEditable(col, row);
51448     },
51449     /**
51450      * Selects a cell.
51451      * @param {Number} field (not used) - as it's normally used as a listener
51452      * @param {Number} e - event - fake it by using
51453      *
51454      * var e = Roo.EventObjectImpl.prototype;
51455      * e.keyCode = e.TAB
51456      *
51457      * 
51458      */
51459     onEditorKey : function(field, e){
51460         
51461         var k = e.getKey(),
51462             newCell,
51463             g = this.grid,
51464             ed = g.activeEditor,
51465             forward = false;
51466         ///Roo.log('onEditorKey' + k);
51467         
51468         
51469         if (this.enter_is_tab && k == e.ENTER) {
51470             k = e.TAB;
51471         }
51472         
51473         if(k == e.TAB){
51474             if(e.shiftKey){
51475                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
51476             }else{
51477                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
51478                 forward = true;
51479             }
51480             
51481             e.stopEvent();
51482             
51483         }else if(k == e.ENTER &&  !e.ctrlKey){
51484             ed.completeEdit();
51485             e.stopEvent();
51486             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
51487         }else if(k == e.ESC){
51488             ed.cancelEdit();
51489         }
51490         
51491         
51492         if(newCell){
51493             //Roo.log('next cell after edit');
51494             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
51495         } else if (forward) {
51496             // tabbed past last
51497             this.fireEvent.defer(100, this, ['tabend',this]);
51498         }
51499     }
51500 });/*
51501  * Based on:
51502  * Ext JS Library 1.1.1
51503  * Copyright(c) 2006-2007, Ext JS, LLC.
51504  *
51505  * Originally Released Under LGPL - original licence link has changed is not relivant.
51506  *
51507  * Fork - LGPL
51508  * <script type="text/javascript">
51509  */
51510  
51511 /**
51512  * @class Roo.grid.EditorGrid
51513  * @extends Roo.grid.Grid
51514  * Class for creating and editable grid.
51515  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
51516  * The container MUST have some type of size defined for the grid to fill. The container will be 
51517  * automatically set to position relative if it isn't already.
51518  * @param {Object} dataSource The data model to bind to
51519  * @param {Object} colModel The column model with info about this grid's columns
51520  */
51521 Roo.grid.EditorGrid = function(container, config){
51522     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
51523     this.getGridEl().addClass("xedit-grid");
51524
51525     if(!this.selModel){
51526         this.selModel = new Roo.grid.CellSelectionModel();
51527     }
51528
51529     this.activeEditor = null;
51530
51531         this.addEvents({
51532             /**
51533              * @event beforeedit
51534              * Fires before cell editing is triggered. The edit event object has the following properties <br />
51535              * <ul style="padding:5px;padding-left:16px;">
51536              * <li>grid - This grid</li>
51537              * <li>record - The record being edited</li>
51538              * <li>field - The field name being edited</li>
51539              * <li>value - The value for the field being edited.</li>
51540              * <li>row - The grid row index</li>
51541              * <li>column - The grid column index</li>
51542              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
51543              * </ul>
51544              * @param {Object} e An edit event (see above for description)
51545              */
51546             "beforeedit" : true,
51547             /**
51548              * @event afteredit
51549              * Fires after a cell is edited. <br />
51550              * <ul style="padding:5px;padding-left:16px;">
51551              * <li>grid - This grid</li>
51552              * <li>record - The record being edited</li>
51553              * <li>field - The field name being edited</li>
51554              * <li>value - The value being set</li>
51555              * <li>originalValue - The original value for the field, before the edit.</li>
51556              * <li>row - The grid row index</li>
51557              * <li>column - The grid column index</li>
51558              * </ul>
51559              * @param {Object} e An edit event (see above for description)
51560              */
51561             "afteredit" : true,
51562             /**
51563              * @event validateedit
51564              * Fires after a cell is edited, but before the value is set in the record. 
51565          * You can use this to modify the value being set in the field, Return false
51566              * to cancel the change. The edit event object has the following properties <br />
51567              * <ul style="padding:5px;padding-left:16px;">
51568          * <li>editor - This editor</li>
51569              * <li>grid - This grid</li>
51570              * <li>record - The record being edited</li>
51571              * <li>field - The field name being edited</li>
51572              * <li>value - The value being set</li>
51573              * <li>originalValue - The original value for the field, before the edit.</li>
51574              * <li>row - The grid row index</li>
51575              * <li>column - The grid column index</li>
51576              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
51577              * </ul>
51578              * @param {Object} e An edit event (see above for description)
51579              */
51580             "validateedit" : true
51581         });
51582     this.on("bodyscroll", this.stopEditing,  this);
51583     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
51584 };
51585
51586 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
51587     /**
51588      * @cfg {Number} clicksToEdit
51589      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
51590      */
51591     clicksToEdit: 2,
51592
51593     // private
51594     isEditor : true,
51595     // private
51596     trackMouseOver: false, // causes very odd FF errors
51597
51598     onCellDblClick : function(g, row, col){
51599         this.startEditing(row, col);
51600     },
51601
51602     onEditComplete : function(ed, value, startValue){
51603         this.editing = false;
51604         this.activeEditor = null;
51605         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
51606         var r = ed.record;
51607         var field = this.colModel.getDataIndex(ed.col);
51608         var e = {
51609             grid: this,
51610             record: r,
51611             field: field,
51612             originalValue: startValue,
51613             value: value,
51614             row: ed.row,
51615             column: ed.col,
51616             cancel:false,
51617             editor: ed
51618         };
51619         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
51620         cell.show();
51621           
51622         if(String(value) !== String(startValue)){
51623             
51624             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
51625                 r.set(field, e.value);
51626                 // if we are dealing with a combo box..
51627                 // then we also set the 'name' colum to be the displayField
51628                 if (ed.field.displayField && ed.field.name) {
51629                     r.set(ed.field.name, ed.field.el.dom.value);
51630                 }
51631                 
51632                 delete e.cancel; //?? why!!!
51633                 this.fireEvent("afteredit", e);
51634             }
51635         } else {
51636             this.fireEvent("afteredit", e); // always fire it!
51637         }
51638         this.view.focusCell(ed.row, ed.col);
51639     },
51640
51641     /**
51642      * Starts editing the specified for the specified row/column
51643      * @param {Number} rowIndex
51644      * @param {Number} colIndex
51645      */
51646     startEditing : function(row, col){
51647         this.stopEditing();
51648         if(this.colModel.isCellEditable(col, row)){
51649             this.view.ensureVisible(row, col, true);
51650           
51651             var r = this.dataSource.getAt(row);
51652             var field = this.colModel.getDataIndex(col);
51653             var cell = Roo.get(this.view.getCell(row,col));
51654             var e = {
51655                 grid: this,
51656                 record: r,
51657                 field: field,
51658                 value: r.data[field],
51659                 row: row,
51660                 column: col,
51661                 cancel:false 
51662             };
51663             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
51664                 this.editing = true;
51665                 var ed = this.colModel.getCellEditor(col, row);
51666                 
51667                 if (!ed) {
51668                     return;
51669                 }
51670                 if(!ed.rendered){
51671                     ed.render(ed.parentEl || document.body);
51672                 }
51673                 ed.field.reset();
51674                
51675                 cell.hide();
51676                 
51677                 (function(){ // complex but required for focus issues in safari, ie and opera
51678                     ed.row = row;
51679                     ed.col = col;
51680                     ed.record = r;
51681                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
51682                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
51683                     this.activeEditor = ed;
51684                     var v = r.data[field];
51685                     ed.startEdit(this.view.getCell(row, col), v);
51686                     // combo's with 'displayField and name set
51687                     if (ed.field.displayField && ed.field.name) {
51688                         ed.field.el.dom.value = r.data[ed.field.name];
51689                     }
51690                     
51691                     
51692                 }).defer(50, this);
51693             }
51694         }
51695     },
51696         
51697     /**
51698      * Stops any active editing
51699      */
51700     stopEditing : function(){
51701         if(this.activeEditor){
51702             this.activeEditor.completeEdit();
51703         }
51704         this.activeEditor = null;
51705     }
51706 });/*
51707  * Based on:
51708  * Ext JS Library 1.1.1
51709  * Copyright(c) 2006-2007, Ext JS, LLC.
51710  *
51711  * Originally Released Under LGPL - original licence link has changed is not relivant.
51712  *
51713  * Fork - LGPL
51714  * <script type="text/javascript">
51715  */
51716
51717 // private - not really -- you end up using it !
51718 // This is a support class used internally by the Grid components
51719
51720 /**
51721  * @class Roo.grid.GridEditor
51722  * @extends Roo.Editor
51723  * Class for creating and editable grid elements.
51724  * @param {Object} config any settings (must include field)
51725  */
51726 Roo.grid.GridEditor = function(field, config){
51727     if (!config && field.field) {
51728         config = field;
51729         field = Roo.factory(config.field, Roo.form);
51730     }
51731     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
51732     field.monitorTab = false;
51733 };
51734
51735 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
51736     
51737     /**
51738      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
51739      */
51740     
51741     alignment: "tl-tl",
51742     autoSize: "width",
51743     hideEl : false,
51744     cls: "x-small-editor x-grid-editor",
51745     shim:false,
51746     shadow:"frame"
51747 });/*
51748  * Based on:
51749  * Ext JS Library 1.1.1
51750  * Copyright(c) 2006-2007, Ext JS, LLC.
51751  *
51752  * Originally Released Under LGPL - original licence link has changed is not relivant.
51753  *
51754  * Fork - LGPL
51755  * <script type="text/javascript">
51756  */
51757   
51758
51759   
51760 Roo.grid.PropertyRecord = Roo.data.Record.create([
51761     {name:'name',type:'string'},  'value'
51762 ]);
51763
51764
51765 Roo.grid.PropertyStore = function(grid, source){
51766     this.grid = grid;
51767     this.store = new Roo.data.Store({
51768         recordType : Roo.grid.PropertyRecord
51769     });
51770     this.store.on('update', this.onUpdate,  this);
51771     if(source){
51772         this.setSource(source);
51773     }
51774     Roo.grid.PropertyStore.superclass.constructor.call(this);
51775 };
51776
51777
51778
51779 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
51780     setSource : function(o){
51781         this.source = o;
51782         this.store.removeAll();
51783         var data = [];
51784         for(var k in o){
51785             if(this.isEditableValue(o[k])){
51786                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
51787             }
51788         }
51789         this.store.loadRecords({records: data}, {}, true);
51790     },
51791
51792     onUpdate : function(ds, record, type){
51793         if(type == Roo.data.Record.EDIT){
51794             var v = record.data['value'];
51795             var oldValue = record.modified['value'];
51796             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
51797                 this.source[record.id] = v;
51798                 record.commit();
51799                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
51800             }else{
51801                 record.reject();
51802             }
51803         }
51804     },
51805
51806     getProperty : function(row){
51807        return this.store.getAt(row);
51808     },
51809
51810     isEditableValue: function(val){
51811         if(val && val instanceof Date){
51812             return true;
51813         }else if(typeof val == 'object' || typeof val == 'function'){
51814             return false;
51815         }
51816         return true;
51817     },
51818
51819     setValue : function(prop, value){
51820         this.source[prop] = value;
51821         this.store.getById(prop).set('value', value);
51822     },
51823
51824     getSource : function(){
51825         return this.source;
51826     }
51827 });
51828
51829 Roo.grid.PropertyColumnModel = function(grid, store){
51830     this.grid = grid;
51831     var g = Roo.grid;
51832     g.PropertyColumnModel.superclass.constructor.call(this, [
51833         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
51834         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
51835     ]);
51836     this.store = store;
51837     this.bselect = Roo.DomHelper.append(document.body, {
51838         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
51839             {tag: 'option', value: 'true', html: 'true'},
51840             {tag: 'option', value: 'false', html: 'false'}
51841         ]
51842     });
51843     Roo.id(this.bselect);
51844     var f = Roo.form;
51845     this.editors = {
51846         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
51847         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
51848         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
51849         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
51850         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
51851     };
51852     this.renderCellDelegate = this.renderCell.createDelegate(this);
51853     this.renderPropDelegate = this.renderProp.createDelegate(this);
51854 };
51855
51856 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
51857     
51858     
51859     nameText : 'Name',
51860     valueText : 'Value',
51861     
51862     dateFormat : 'm/j/Y',
51863     
51864     
51865     renderDate : function(dateVal){
51866         return dateVal.dateFormat(this.dateFormat);
51867     },
51868
51869     renderBool : function(bVal){
51870         return bVal ? 'true' : 'false';
51871     },
51872
51873     isCellEditable : function(colIndex, rowIndex){
51874         return colIndex == 1;
51875     },
51876
51877     getRenderer : function(col){
51878         return col == 1 ?
51879             this.renderCellDelegate : this.renderPropDelegate;
51880     },
51881
51882     renderProp : function(v){
51883         return this.getPropertyName(v);
51884     },
51885
51886     renderCell : function(val){
51887         var rv = val;
51888         if(val instanceof Date){
51889             rv = this.renderDate(val);
51890         }else if(typeof val == 'boolean'){
51891             rv = this.renderBool(val);
51892         }
51893         return Roo.util.Format.htmlEncode(rv);
51894     },
51895
51896     getPropertyName : function(name){
51897         var pn = this.grid.propertyNames;
51898         return pn && pn[name] ? pn[name] : name;
51899     },
51900
51901     getCellEditor : function(colIndex, rowIndex){
51902         var p = this.store.getProperty(rowIndex);
51903         var n = p.data['name'], val = p.data['value'];
51904         
51905         if(typeof(this.grid.customEditors[n]) == 'string'){
51906             return this.editors[this.grid.customEditors[n]];
51907         }
51908         if(typeof(this.grid.customEditors[n]) != 'undefined'){
51909             return this.grid.customEditors[n];
51910         }
51911         if(val instanceof Date){
51912             return this.editors['date'];
51913         }else if(typeof val == 'number'){
51914             return this.editors['number'];
51915         }else if(typeof val == 'boolean'){
51916             return this.editors['boolean'];
51917         }else{
51918             return this.editors['string'];
51919         }
51920     }
51921 });
51922
51923 /**
51924  * @class Roo.grid.PropertyGrid
51925  * @extends Roo.grid.EditorGrid
51926  * This class represents the  interface of a component based property grid control.
51927  * <br><br>Usage:<pre><code>
51928  var grid = new Roo.grid.PropertyGrid("my-container-id", {
51929       
51930  });
51931  // set any options
51932  grid.render();
51933  * </code></pre>
51934   
51935  * @constructor
51936  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
51937  * The container MUST have some type of size defined for the grid to fill. The container will be
51938  * automatically set to position relative if it isn't already.
51939  * @param {Object} config A config object that sets properties on this grid.
51940  */
51941 Roo.grid.PropertyGrid = function(container, config){
51942     config = config || {};
51943     var store = new Roo.grid.PropertyStore(this);
51944     this.store = store;
51945     var cm = new Roo.grid.PropertyColumnModel(this, store);
51946     store.store.sort('name', 'ASC');
51947     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
51948         ds: store.store,
51949         cm: cm,
51950         enableColLock:false,
51951         enableColumnMove:false,
51952         stripeRows:false,
51953         trackMouseOver: false,
51954         clicksToEdit:1
51955     }, config));
51956     this.getGridEl().addClass('x-props-grid');
51957     this.lastEditRow = null;
51958     this.on('columnresize', this.onColumnResize, this);
51959     this.addEvents({
51960          /**
51961              * @event beforepropertychange
51962              * Fires before a property changes (return false to stop?)
51963              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
51964              * @param {String} id Record Id
51965              * @param {String} newval New Value
51966          * @param {String} oldval Old Value
51967              */
51968         "beforepropertychange": true,
51969         /**
51970              * @event propertychange
51971              * Fires after a property changes
51972              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
51973              * @param {String} id Record Id
51974              * @param {String} newval New Value
51975          * @param {String} oldval Old Value
51976              */
51977         "propertychange": true
51978     });
51979     this.customEditors = this.customEditors || {};
51980 };
51981 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
51982     
51983      /**
51984      * @cfg {Object} customEditors map of colnames=> custom editors.
51985      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
51986      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
51987      * false disables editing of the field.
51988          */
51989     
51990       /**
51991      * @cfg {Object} propertyNames map of property Names to their displayed value
51992          */
51993     
51994     render : function(){
51995         Roo.grid.PropertyGrid.superclass.render.call(this);
51996         this.autoSize.defer(100, this);
51997     },
51998
51999     autoSize : function(){
52000         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
52001         if(this.view){
52002             this.view.fitColumns();
52003         }
52004     },
52005
52006     onColumnResize : function(){
52007         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
52008         this.autoSize();
52009     },
52010     /**
52011      * Sets the data for the Grid
52012      * accepts a Key => Value object of all the elements avaiable.
52013      * @param {Object} data  to appear in grid.
52014      */
52015     setSource : function(source){
52016         this.store.setSource(source);
52017         //this.autoSize();
52018     },
52019     /**
52020      * Gets all the data from the grid.
52021      * @return {Object} data  data stored in grid
52022      */
52023     getSource : function(){
52024         return this.store.getSource();
52025     }
52026 });/*
52027  * Based on:
52028  * Ext JS Library 1.1.1
52029  * Copyright(c) 2006-2007, Ext JS, LLC.
52030  *
52031  * Originally Released Under LGPL - original licence link has changed is not relivant.
52032  *
52033  * Fork - LGPL
52034  * <script type="text/javascript">
52035  */
52036  
52037 /**
52038  * @class Roo.LoadMask
52039  * A simple utility class for generically masking elements while loading data.  If the element being masked has
52040  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
52041  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
52042  * element's UpdateManager load indicator and will be destroyed after the initial load.
52043  * @constructor
52044  * Create a new LoadMask
52045  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
52046  * @param {Object} config The config object
52047  */
52048 Roo.LoadMask = function(el, config){
52049     this.el = Roo.get(el);
52050     Roo.apply(this, config);
52051     if(this.store){
52052         this.store.on('beforeload', this.onBeforeLoad, this);
52053         this.store.on('load', this.onLoad, this);
52054         this.store.on('loadexception', this.onLoadException, this);
52055         this.removeMask = false;
52056     }else{
52057         var um = this.el.getUpdateManager();
52058         um.showLoadIndicator = false; // disable the default indicator
52059         um.on('beforeupdate', this.onBeforeLoad, this);
52060         um.on('update', this.onLoad, this);
52061         um.on('failure', this.onLoad, this);
52062         this.removeMask = true;
52063     }
52064 };
52065
52066 Roo.LoadMask.prototype = {
52067     /**
52068      * @cfg {Boolean} removeMask
52069      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
52070      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
52071      */
52072     /**
52073      * @cfg {String} msg
52074      * The text to display in a centered loading message box (defaults to 'Loading...')
52075      */
52076     msg : 'Loading...',
52077     /**
52078      * @cfg {String} msgCls
52079      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
52080      */
52081     msgCls : 'x-mask-loading',
52082
52083     /**
52084      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
52085      * @type Boolean
52086      */
52087     disabled: false,
52088
52089     /**
52090      * Disables the mask to prevent it from being displayed
52091      */
52092     disable : function(){
52093        this.disabled = true;
52094     },
52095
52096     /**
52097      * Enables the mask so that it can be displayed
52098      */
52099     enable : function(){
52100         this.disabled = false;
52101     },
52102     
52103     onLoadException : function()
52104     {
52105         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
52106             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
52107         }
52108         this.el.unmask(this.removeMask);
52109     },
52110     // private
52111     onLoad : function()
52112     {
52113         this.el.unmask(this.removeMask);
52114     },
52115
52116     // private
52117     onBeforeLoad : function(){
52118         if(!this.disabled){
52119             this.el.mask(this.msg, this.msgCls);
52120         }
52121     },
52122
52123     // private
52124     destroy : function(){
52125         if(this.store){
52126             this.store.un('beforeload', this.onBeforeLoad, this);
52127             this.store.un('load', this.onLoad, this);
52128             this.store.un('loadexception', this.onLoadException, this);
52129         }else{
52130             var um = this.el.getUpdateManager();
52131             um.un('beforeupdate', this.onBeforeLoad, this);
52132             um.un('update', this.onLoad, this);
52133             um.un('failure', this.onLoad, this);
52134         }
52135     }
52136 };/*
52137  * Based on:
52138  * Ext JS Library 1.1.1
52139  * Copyright(c) 2006-2007, Ext JS, LLC.
52140  *
52141  * Originally Released Under LGPL - original licence link has changed is not relivant.
52142  *
52143  * Fork - LGPL
52144  * <script type="text/javascript">
52145  */
52146 Roo.XTemplate = function(){
52147     Roo.XTemplate.superclass.constructor.apply(this, arguments);
52148     var s = this.html;
52149
52150     s = ['<tpl>', s, '</tpl>'].join('');
52151
52152     var re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/;
52153
52154     var nameRe = /^<tpl\b[^>]*?for="(.*?)"/;
52155     var ifRe = /^<tpl\b[^>]*?if="(.*?)"/;
52156     var execRe = /^<tpl\b[^>]*?exec="(.*?)"/;
52157     var m, id = 0;
52158     var tpls = [];
52159
52160     while(m = s.match(re)){
52161        var m2 = m[0].match(nameRe);
52162        var m3 = m[0].match(ifRe);
52163        var m4 = m[0].match(execRe);
52164        var exp = null, fn = null, exec = null;
52165        var name = m2 && m2[1] ? m2[1] : '';
52166        if(m3){
52167            exp = m3 && m3[1] ? m3[1] : null;
52168            if(exp){
52169                fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
52170            }
52171        }
52172        if(m4){
52173            exp = m4 && m4[1] ? m4[1] : null;
52174            if(exp){
52175                exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
52176            }
52177        }
52178        if(name){
52179            switch(name){
52180                case '.': name = new Function('values', 'parent', 'with(values){ return values; }'); break;
52181                case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
52182                default: name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
52183            }
52184        }
52185        tpls.push({
52186             id: id,
52187             target: name,
52188             exec: exec,
52189             test: fn,
52190             body: m[1]||''
52191         });
52192        s = s.replace(m[0], '{xtpl'+ id + '}');
52193        ++id;
52194     }
52195     for(var i = tpls.length-1; i >= 0; --i){
52196         this.compileTpl(tpls[i]);
52197     }
52198     this.master = tpls[tpls.length-1];
52199     this.tpls = tpls;
52200 };
52201 Roo.extend(Roo.XTemplate, Roo.Template, {
52202
52203     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
52204
52205     applySubTemplate : function(id, values, parent){
52206         var t = this.tpls[id];
52207         if(t.test && !t.test.call(this, values, parent)){
52208             return '';
52209         }
52210         if(t.exec && t.exec.call(this, values, parent)){
52211             return '';
52212         }
52213         var vs = t.target ? t.target.call(this, values, parent) : values;
52214         parent = t.target ? values : parent;
52215         if(t.target && vs instanceof Array){
52216             var buf = [];
52217             for(var i = 0, len = vs.length; i < len; i++){
52218                 buf[buf.length] = t.compiled.call(this, vs[i], parent);
52219             }
52220             return buf.join('');
52221         }
52222         return t.compiled.call(this, vs, parent);
52223     },
52224
52225     compileTpl : function(tpl){
52226         var fm = Roo.util.Format;
52227         var useF = this.disableFormats !== true;
52228         var sep = Roo.isGecko ? "+" : ",";
52229         var fn = function(m, name, format, args){
52230             if(name.substr(0, 4) == 'xtpl'){
52231                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
52232             }
52233             var v;
52234             if(name.indexOf('.') != -1){
52235                 v = name;
52236             }else{
52237                 v = "values['" + name + "']";
52238             }
52239             if(format && useF){
52240                 args = args ? ',' + args : "";
52241                 if(format.substr(0, 5) != "this."){
52242                     format = "fm." + format + '(';
52243                 }else{
52244                     format = 'this.call("'+ format.substr(5) + '", ';
52245                     args = ", values";
52246                 }
52247             }else{
52248                 args= ''; format = "("+v+" === undefined ? '' : ";
52249             }
52250             return "'"+ sep + format + v + args + ")"+sep+"'";
52251         };
52252         var body;
52253         // branched to use + in gecko and [].join() in others
52254         if(Roo.isGecko){
52255             body = "tpl.compiled = function(values, parent){ return '" +
52256                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
52257                     "';};";
52258         }else{
52259             body = ["tpl.compiled = function(values, parent){ return ['"];
52260             body.push(tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
52261             body.push("'].join('');};");
52262             body = body.join('');
52263         }
52264         /** eval:var:zzzzzzz */
52265         eval(body);
52266         return this;
52267     },
52268
52269     applyTemplate : function(values){
52270         return this.master.compiled.call(this, values, {});
52271         var s = this.subs;
52272     },
52273
52274     apply : function(){
52275         return this.applyTemplate.apply(this, arguments);
52276     },
52277
52278     compile : function(){return this;}
52279 });
52280
52281 Roo.XTemplate.from = function(el){
52282     el = Roo.getDom(el);
52283     return new Roo.XTemplate(el.value || el.innerHTML);
52284 };/*
52285  * Original code for Roojs - LGPL
52286  * <script type="text/javascript">
52287  */
52288  
52289 /**
52290  * @class Roo.XComponent
52291  * A delayed Element creator...
52292  * Or a way to group chunks of interface together.
52293  * 
52294  * Mypart.xyx = new Roo.XComponent({
52295
52296     parent : 'Mypart.xyz', // empty == document.element.!!
52297     order : '001',
52298     name : 'xxxx'
52299     region : 'xxxx'
52300     disabled : function() {} 
52301      
52302     tree : function() { // return an tree of xtype declared components
52303         var MODULE = this;
52304         return 
52305         {
52306             xtype : 'NestedLayoutPanel',
52307             // technicall
52308         }
52309      ]
52310  *})
52311  *
52312  *
52313  * It can be used to build a big heiracy, with parent etc.
52314  * or you can just use this to render a single compoent to a dom element
52315  * MYPART.render(Roo.Element | String(id) | dom_element )
52316  * 
52317  * @extends Roo.util.Observable
52318  * @constructor
52319  * @param cfg {Object} configuration of component
52320  * 
52321  */
52322 Roo.XComponent = function(cfg) {
52323     Roo.apply(this, cfg);
52324     this.addEvents({ 
52325         /**
52326              * @event built
52327              * Fires when this the componnt is built
52328              * @param {Roo.XComponent} c the component
52329              */
52330         'built' : true,
52331         /**
52332              * @event buildcomplete
52333              * Fires on the top level element when all elements have been built
52334              * @param {Roo.XComponent} c the top level component.
52335          */
52336         'buildcomplete' : true
52337         
52338     });
52339     this.region = this.region || 'center'; // default..
52340     Roo.XComponent.register(this);
52341     this.modules = false;
52342     this.el = false; // where the layout goes..
52343     
52344     
52345 }
52346 Roo.extend(Roo.XComponent, Roo.util.Observable, {
52347     /**
52348      * @property el
52349      * The created element (with Roo.factory())
52350      * @type {Roo.Layout}
52351      */
52352     el  : false,
52353     
52354     /**
52355      * @property el
52356      * for BC  - use el in new code
52357      * @type {Roo.Layout}
52358      */
52359     panel : false,
52360     
52361     /**
52362      * @property layout
52363      * for BC  - use el in new code
52364      * @type {Roo.Layout}
52365      */
52366     layout : false,
52367     
52368      /**
52369      * @cfg {Function|boolean} disabled
52370      * If this module is disabled by some rule, return true from the funtion
52371      */
52372     disabled : false,
52373     
52374     /**
52375      * @cfg {String} parent 
52376      * Name of parent element which it get xtype added to..
52377      */
52378     parent: false,
52379     
52380     /**
52381      * @cfg {String} order
52382      * Used to set the order in which elements are created (usefull for multiple tabs)
52383      */
52384     
52385     order : false,
52386     /**
52387      * @cfg {String} name
52388      * String to display while loading.
52389      */
52390     name : false,
52391     /**
52392      * @cfg {String} region
52393      * Region to render component to (defaults to center)
52394      */
52395     region : 'center',
52396     
52397     /**
52398      * @cfg {Array} items
52399      * A single item array - the first element is the root of the tree..
52400      * It's done this way to stay compatible with the Xtype system...
52401      */
52402     items : false,
52403     
52404     
52405      /**
52406      * render
52407      * render element to dom or tree
52408      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
52409      */
52410     
52411     render : function(el)
52412     {
52413         
52414         el = el || false;
52415         var hp = this.parent ? 1 : 0;
52416         
52417         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
52418             // if parent is a '#.....' string, then let's use that..
52419             var ename = this.parent.substr(1)
52420             this.parent = false;
52421             el = Roo.get(ename);
52422             if (!el) {
52423                 Roo.log("Warning - element can not be found :#" + ename );
52424                 return;
52425             }
52426         }
52427         
52428         
52429         if (!this.parent) {
52430             
52431             el = el ? Roo.get(el) : false;
52432             
52433             // it's a top level one..
52434             this.parent =  {
52435                 el : new Roo.BorderLayout(el || document.body, {
52436                 
52437                      center: {
52438                          titlebar: false,
52439                          autoScroll:false,
52440                          closeOnTab: true,
52441                          tabPosition: 'top',
52442                           //resizeTabs: true,
52443                          alwaysShowTabs: el && hp? false :  true,
52444                          hideTabs: el || !hp ? true :  false,
52445                          minTabWidth: 140
52446                      }
52447                  })
52448             }
52449         }
52450         
52451         
52452             
52453         var tree = this.tree();
52454         tree.region = tree.region || this.region;
52455         this.el = this.parent.el.addxtype(tree);
52456         this.fireEvent('built', this);
52457         
52458         this.panel = this.el;
52459         this.layout = this.panel.layout;    
52460          
52461     }
52462     
52463 });
52464
52465 Roo.apply(Roo.XComponent, {
52466     
52467     /**
52468      * @property  buildCompleted
52469      * True when the builder has completed building the interface.
52470      * @type Boolean
52471      */
52472     buildCompleted : false,
52473      
52474     /**
52475      * @property  topModule
52476      * the upper most module - uses document.element as it's constructor.
52477      * @type Object
52478      */
52479      
52480     topModule  : false,
52481       
52482     /**
52483      * @property  modules
52484      * array of modules to be created by registration system.
52485      * @type {Array} of Roo.XComponent
52486      */
52487     
52488     modules : [],
52489     /**
52490      * @property  elmodules
52491      * array of modules to be created by which use #ID 
52492      * @type {Array} of Roo.XComponent
52493      */
52494      
52495     elmodules : [],
52496
52497     
52498     /**
52499      * Register components to be built later.
52500      *
52501      * This solves the following issues
52502      * - Building is not done on page load, but after an authentication process has occured.
52503      * - Interface elements are registered on page load
52504      * - Parent Interface elements may not be loaded before child, so this handles that..
52505      * 
52506      *
52507      * example:
52508      * 
52509      * MyApp.register({
52510           order : '000001',
52511           module : 'Pman.Tab.projectMgr',
52512           region : 'center',
52513           parent : 'Pman.layout',
52514           disabled : false,  // or use a function..
52515         })
52516      
52517      * * @param {Object} details about module
52518      */
52519     register : function(obj) {
52520         this.modules.push(obj);
52521          
52522     },
52523     /**
52524      * convert a string to an object..
52525      * eg. 'AAA.BBB' -> finds AAA.BBB
52526
52527      */
52528     
52529     toObject : function(str)
52530     {
52531         if (!str || typeof(str) == 'object') {
52532             return str;
52533         }
52534         if (str.substring(0,1) == '#') {
52535             return str;
52536         }
52537
52538         var ar = str.split('.');
52539         var rt, o;
52540         rt = ar.shift();
52541             /** eval:var:o */
52542         try {
52543             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
52544         } catch (e) {
52545             throw "Module not found : " + str;
52546         }
52547         
52548         if (o === false) {
52549             throw "Module not found : " + str;
52550         }
52551         Roo.each(ar, function(e) {
52552             if (typeof(o[e]) == 'undefined') {
52553                 throw "Module not found : " + str;
52554             }
52555             o = o[e];
52556         });
52557         
52558         return o;
52559         
52560     },
52561     
52562     
52563     /**
52564      * move modules into their correct place in the tree..
52565      * 
52566      */
52567     preBuild : function ()
52568     {
52569         var _t = this;
52570         Roo.each(this.modules , function (obj)
52571         {
52572             var opar = obj.parent;
52573             try { 
52574                 obj.parent = this.toObject(opar);
52575             } catch(e) {
52576                 Roo.log(e.toString());
52577                 return;
52578             }
52579             
52580             if (!obj.parent) {
52581                 this.topModule = obj;
52582                 return;
52583             }
52584             if (typeof(obj.parent) == 'string') {
52585                 this.elmodules.push(obj);
52586                 return;
52587             }
52588             if (obj.parent.constructor != Roo.XComponent) {
52589                 Roo.log("Object Parent is not instance of XComponent:" + obj.name)
52590             }
52591             if (!obj.parent.modules) {
52592                 obj.parent.modules = new Roo.util.MixedCollection(false, 
52593                     function(o) { return o.order + '' }
52594                 );
52595             }
52596             
52597             obj.parent.modules.add(obj);
52598         }, this);
52599     },
52600     
52601      /**
52602      * make a list of modules to build.
52603      * @return {Array} list of modules. 
52604      */ 
52605     
52606     buildOrder : function()
52607     {
52608         var _this = this;
52609         var cmp = function(a,b) {   
52610             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
52611         };
52612         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
52613             throw "No top level modules to build";
52614         }
52615         
52616         // make a flat list in order of modules to build.
52617         var mods = this.topModule ? [ this.topModule ] : [];
52618         Roo.each(this.elmodules,function(e) { mods.push(e) });
52619
52620         
52621         // add modules to their parents..
52622         var addMod = function(m) {
52623            // Roo.debug && Roo.log(m.modKey);
52624             
52625             mods.push(m);
52626             if (m.modules) {
52627                 m.modules.keySort('ASC',  cmp );
52628                 m.modules.each(addMod);
52629             }
52630             // not sure if this is used any more..
52631             if (m.finalize) {
52632                 m.finalize.name = m.name + " (clean up) ";
52633                 mods.push(m.finalize);
52634             }
52635             
52636         }
52637         if (this.topModule) { 
52638             this.topModule.modules.keySort('ASC',  cmp );
52639             this.topModule.modules.each(addMod);
52640         }
52641         return mods;
52642     },
52643     
52644      /**
52645      * Build the registered modules.
52646      * @param {Object} parent element.
52647      * @param {Function} optional method to call after module has been added.
52648      * 
52649      */ 
52650    
52651     build : function() 
52652     {
52653         
52654         this.preBuild();
52655         var mods = this.buildOrder();
52656       
52657         //this.allmods = mods;
52658         //Roo.debug && Roo.log(mods);
52659         //return;
52660         if (!mods.length) { // should not happen
52661             throw "NO modules!!!";
52662         }
52663         
52664         
52665         
52666         // flash it up as modal - so we store the mask!?
52667         Roo.MessageBox.show({ title: 'loading' });
52668         Roo.MessageBox.show({
52669            title: "Please wait...",
52670            msg: "Building Interface...",
52671            width:450,
52672            progress:true,
52673            closable:false,
52674            modal: false
52675           
52676         });
52677         var total = mods.length;
52678         
52679         var _this = this;
52680         var progressRun = function() {
52681             if (!mods.length) {
52682                 Roo.debug && Roo.log('hide?');
52683                 Roo.MessageBox.hide();
52684                 if (_this.topModule) { 
52685                     _this.topModule.fireEvent('buildcomplete', _this.topModule);
52686                 }
52687                 // THE END...
52688                 return false;   
52689             }
52690             
52691             var m = mods.shift();
52692             
52693             
52694             Roo.debug && Roo.log(m);
52695             // not sure if this is supported any more.. - modules that are are just function
52696             if (typeof(m) == 'function') { 
52697                 m.call(this);
52698                 return progressRun.defer(10, _this);
52699             } 
52700             
52701             
52702             
52703             Roo.MessageBox.updateProgress(
52704                 (total  - mods.length)/total,  "Building Interface " + (total  - mods.length) + 
52705                     " of " + total + 
52706                     (m.name ? (' - ' + m.name) : '')
52707                     );
52708             
52709          
52710             // is the module disabled?
52711             var disabled = (typeof(m.disabled) == 'function') ?
52712                 m.disabled.call(m.module.disabled) : m.disabled;    
52713             
52714             
52715             if (disabled) {
52716                 return progressRun(); // we do not update the display!
52717             }
52718             
52719             // now build 
52720             
52721             m.render();
52722             // it's 10 on top level, and 1 on others??? why...
52723             return progressRun.defer(10, _this);
52724              
52725         }
52726         progressRun.defer(1, _this);
52727      
52728         
52729         
52730     }
52731     
52732      
52733    
52734     
52735     
52736 });
52737  //<script type="text/javascript">
52738
52739
52740 /**
52741  * @class Roo.Login
52742  * @extends Roo.LayoutDialog
52743  * A generic Login Dialog..... - only one needed in theory!?!?
52744  *
52745  * Fires XComponent builder on success...
52746  * 
52747  * Sends 
52748  *    username,password, lang = for login actions.
52749  *    check = 1 for periodic checking that sesion is valid.
52750  *    passwordRequest = email request password
52751  *    logout = 1 = to logout
52752  * 
52753  * Affects: (this id="????" elements)
52754  *   loading  (removed) (used to indicate application is loading)
52755  *   loading-mask (hides) (used to hide application when it's building loading)
52756  *   
52757  * 
52758  * Usage: 
52759  *    
52760  * 
52761  * Myapp.login = Roo.Login({
52762      url: xxxx,
52763    
52764      realm : 'Myapp', 
52765      
52766      
52767      method : 'POST',
52768      
52769      
52770      * 
52771  })
52772  * 
52773  * 
52774  * 
52775  **/
52776  
52777 Roo.Login = function(cfg)
52778 {
52779     this.addEvents({
52780         'refreshed' : true
52781     });
52782     
52783     Roo.apply(this,cfg);
52784     
52785     Roo.onReady(function() {
52786         this.onLoad();
52787     }, this);
52788     // call parent..
52789     
52790    
52791     Roo.Login.superclass.constructor.call(this, this);
52792     //this.addxtype(this.items[0]);
52793     
52794     
52795 }
52796
52797
52798 Roo.extend(Roo.Login, Roo.LayoutDialog, {
52799     
52800     /**
52801      * @cfg {String} method
52802      * Method used to query for login details.
52803      */
52804     
52805     method : 'POST',
52806     /**
52807      * @cfg {String} url
52808      * URL to query login data. - eg. baseURL + '/Login.php'
52809      */
52810     url : '',
52811     
52812     /**
52813      * @property user
52814      * The user data - if user.id < 0 then login will be bypassed. (used for inital setup situation.
52815      * @type {Object} 
52816      */
52817     user : false,
52818     /**
52819      * @property checkFails
52820      * Number of times we have attempted to get authentication check, and failed.
52821      * @type {Number} 
52822      */
52823     checkFails : 0,
52824       /**
52825      * @property intervalID
52826      * The window interval that does the constant login checking.
52827      * @type {Number} 
52828      */
52829     intervalID : 0,
52830     
52831     
52832     onLoad : function() // called on page load...
52833     {
52834         // load 
52835          
52836         if (Roo.get('loading')) { // clear any loading indicator..
52837             Roo.get('loading').remove();
52838         }
52839         
52840         //this.switchLang('en'); // set the language to english..
52841        
52842         this.check({
52843             success:  function(response, opts)  {  // check successfull...
52844             
52845                 var res = this.processResponse(response);
52846                 this.checkFails =0;
52847                 if (!res.success) { // error!
52848                     this.checkFails = 5;
52849                     //console.log('call failure');
52850                     return this.failure(response,opts);
52851                 }
52852                 
52853                 if (!res.data.id) { // id=0 == login failure.
52854                     return this.show();
52855                 }
52856                 
52857                               
52858                         //console.log(success);
52859                 this.fillAuth(res.data);   
52860                 this.checkFails =0;
52861                 Roo.XComponent.build();
52862             },
52863             failure : this.show
52864         });
52865         
52866     }, 
52867     
52868     
52869     check: function(cfg) // called every so often to refresh cookie etc..
52870     {
52871         if (cfg.again) { // could be undefined..
52872             this.checkFails++;
52873         } else {
52874             this.checkFails = 0;
52875         }
52876         var _this = this;
52877         if (this.sending) {
52878             if ( this.checkFails > 4) {
52879                 Roo.MessageBox.alert("Error",  
52880                     "Error getting authentication status. - try reloading, or wait a while", function() {
52881                         _this.sending = false;
52882                     }); 
52883                 return;
52884             }
52885             cfg.again = true;
52886             _this.check.defer(10000, _this, [ cfg ]); // check in 10 secs.
52887             return;
52888         }
52889         this.sending = true;
52890         
52891         Roo.Ajax.request({  
52892             url: this.url,
52893             params: {
52894                 getAuthUser: true
52895             },  
52896             method: this.method,
52897             success:  cfg.success || this.success,
52898             failure : cfg.failure || this.failure,
52899             scope : this,
52900             callCfg : cfg
52901               
52902         });  
52903     }, 
52904     
52905     
52906     logout: function()
52907     {
52908         window.onbeforeunload = function() { }; // false does not work for IE..
52909         this.user = false;
52910         var _this = this;
52911         
52912         Roo.Ajax.request({  
52913             url: this.url,
52914             params: {
52915                 logout: 1
52916             },  
52917             method: 'GET',
52918             failure : function() {
52919                 Roo.MessageBox.alert("Error", "Error logging out. - continuing anyway.", function() {
52920                     document.location = document.location.toString() + '?ts=' + Math.random();
52921                 });
52922                 
52923             },
52924             success : function() {
52925                 _this.user = false;
52926                 this.checkFails =0;
52927                 // fixme..
52928                 document.location = document.location.toString() + '?ts=' + Math.random();
52929             }
52930               
52931               
52932         }); 
52933     },
52934     
52935     processResponse : function (response)
52936     {
52937         var res = '';
52938         try {
52939             res = Roo.decode(response.responseText);
52940             // oops...
52941             if (typeof(res) != 'object') {
52942                 res = { success : false, errorMsg : res, errors : true };
52943             }
52944             if (typeof(res.success) == 'undefined') {
52945                 res.success = false;
52946             }
52947             
52948         } catch(e) {
52949             res = { success : false,  errorMsg : response.responseText, errors : true };
52950         }
52951         return res;
52952     },
52953     
52954     success : function(response, opts)  // check successfull...
52955     {  
52956         this.sending = false;
52957         var res = this.processResponse(response);
52958         if (!res.success) {
52959             return this.failure(response, opts);
52960         }
52961         if (!res.data || !res.data.id) {
52962             return this.failure(response,opts);
52963         }
52964         //console.log(res);
52965         this.fillAuth(res.data);
52966         
52967         this.checkFails =0;
52968         
52969     },
52970     
52971     
52972     failure : function (response, opts) // called if login 'check' fails.. (causes re-check)
52973     {
52974         this.authUser = -1;
52975         this.sending = false;
52976         var res = this.processResponse(response);
52977         //console.log(res);
52978         if ( this.checkFails > 2) {
52979         
52980             Roo.MessageBox.alert("Error", res.errorMsg ? res.errorMsg : 
52981                 "Error getting authentication status. - try reloading"); 
52982             return;
52983         }
52984         opts.callCfg.again = true;
52985         this.check.defer(1000, this, [ opts.callCfg ]);
52986         return;  
52987     },
52988     
52989     
52990     
52991     fillAuth: function(au) {
52992         this.startAuthCheck();
52993         this.authUserId = au.id;
52994         this.authUser = au;
52995         this.lastChecked = new Date();
52996         this.fireEvent('refreshed', au);
52997         //Pman.Tab.FaxQueue.newMaxId(au.faxMax);
52998         //Pman.Tab.FaxTab.setTitle(au.faxNumPending);
52999         au.lang = au.lang || 'en';
53000         //this.switchLang(Roo.state.Manager.get('Pman.Login.lang', 'en'));
53001         Roo.state.Manager.set( this.realm + 'lang' , au.lang);
53002         this.switchLang(au.lang );
53003         
53004      
53005         // open system... - -on setyp..
53006         if (this.authUserId  < 0) {
53007             Roo.MessageBox.alert("Warning", 
53008                 "This is an open system - please set up a admin user with a password.");  
53009         }
53010          
53011         //Pman.onload(); // which should do nothing if it's a re-auth result...
53012         
53013              
53014     },
53015     
53016     startAuthCheck : function() // starter for timeout checking..
53017     {
53018         if (this.intervalID) { // timer already in place...
53019             return false;
53020         }
53021         var _this = this;
53022         this.intervalID =  window.setInterval(function() {
53023               _this.check(false);
53024             }, 120000); // every 120 secs = 2mins..
53025         
53026         
53027     },
53028          
53029     
53030     switchLang : function (lang) 
53031     {
53032         _T = typeof(_T) == 'undefined' ? false : _T;
53033           if (!_T || !lang.length) {
53034             return;
53035         }
53036         
53037         if (!_T && lang != 'en') {
53038             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
53039             return;
53040         }
53041         
53042         if (typeof(_T.en) == 'undefined') {
53043             _T.en = {};
53044             Roo.apply(_T.en, _T);
53045         }
53046         
53047         if (typeof(_T[lang]) == 'undefined') {
53048             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
53049             return;
53050         }
53051         
53052         
53053         Roo.apply(_T, _T[lang]);
53054         // just need to set the text values for everything...
53055         var _this = this;
53056         /* this will not work ...
53057         if (this.form) { 
53058             
53059                
53060             function formLabel(name, val) {
53061                 _this.form.findField(name).fieldEl.child('label').dom.innerHTML  = val;
53062             }
53063             
53064             formLabel('password', "Password"+':');
53065             formLabel('username', "Email Address"+':');
53066             formLabel('lang', "Language"+':');
53067             this.dialog.setTitle("Login");
53068             this.dialog.buttons[0].setText("Forgot Password");
53069             this.dialog.buttons[1].setText("Login");
53070         }
53071         */
53072         
53073         
53074     },
53075     
53076     
53077     title: "Login",
53078     modal: true,
53079     width:  350,
53080     //height: 230,
53081     height: 180,
53082     shadow: true,
53083     minWidth:200,
53084     minHeight:180,
53085     //proxyDrag: true,
53086     closable: false,
53087     draggable: false,
53088     collapsible: false,
53089     resizable: false,
53090     center: {  // needed??
53091         autoScroll:false,
53092         titlebar: false,
53093        // tabPosition: 'top',
53094         hideTabs: true,
53095         closeOnTab: true,
53096         alwaysShowTabs: false
53097     } ,
53098     listeners : {
53099         
53100         show  : function(dlg)
53101         {
53102             //console.log(this);
53103             this.form = this.layout.getRegion('center').activePanel.form;
53104             this.form.dialog = dlg;
53105             this.buttons[0].form = this.form;
53106             this.buttons[0].dialog = dlg;
53107             this.buttons[1].form = this.form;
53108             this.buttons[1].dialog = dlg;
53109            
53110            //this.resizeToLogo.defer(1000,this);
53111             // this is all related to resizing for logos..
53112             //var sz = Roo.get(Pman.Login.form.el.query('img')[0]).getSize();
53113            //// if (!sz) {
53114              //   this.resizeToLogo.defer(1000,this);
53115              //   return;
53116            // }
53117             //var w = Ext.lib.Dom.getViewWidth() - 100;
53118             //var h = Ext.lib.Dom.getViewHeight() - 100;
53119             //this.resizeTo(Math.max(350, Math.min(sz.width + 30, w)),Math.min(sz.height+200, h));
53120             //this.center();
53121             if (this.disabled) {
53122                 this.hide();
53123                 return;
53124             }
53125             
53126             if (this.user.id < 0) { // used for inital setup situations.
53127                 return;
53128             }
53129             
53130             if (this.intervalID) {
53131                 // remove the timer
53132                 window.clearInterval(this.intervalID);
53133                 this.intervalID = false;
53134             }
53135             
53136             
53137             if (Roo.get('loading')) {
53138                 Roo.get('loading').remove();
53139             }
53140             if (Roo.get('loading-mask')) {
53141                 Roo.get('loading-mask').hide();
53142             }
53143             
53144             //incomming._node = tnode;
53145             this.form.reset();
53146             //this.dialog.modal = !modal;
53147             //this.dialog.show();
53148             this.el.unmask(); 
53149             
53150             
53151             this.form.setValues({
53152                 'username' : Roo.state.Manager.get(this.realm + '.username', ''),
53153                 'lang' : Roo.state.Manager.get(this.realm + '.lang', 'en')
53154             });
53155             
53156             this.switchLang(Roo.state.Manager.get(this.realm + '.lang', 'en'));
53157             if (this.form.findField('username').getValue().length > 0 ){
53158                 this.form.findField('password').focus();
53159             } else {
53160                this.form.findField('username').focus();
53161             }
53162     
53163         }
53164     },
53165     items : [
53166          {
53167        
53168             xtype : 'ContentPanel',
53169             xns : Roo,
53170             region: 'center',
53171             fitToFrame : true,
53172             
53173             items : [
53174     
53175                 {
53176                
53177                     xtype : 'Form',
53178                     xns : Roo.form,
53179                     labelWidth: 100,
53180                     style : 'margin: 10px;',
53181                     
53182                     listeners : {
53183                         actionfailed : function(f, act) {
53184                             // form can return { errors: .... }
53185                                 
53186                             //act.result.errors // invalid form element list...
53187                             //act.result.errorMsg// invalid form element list...
53188                             
53189                             this.dialog.el.unmask();
53190                             Roo.MessageBox.alert("Error", act.result.errorMsg ? act.result.errorMsg : 
53191                                         "Login failed - communication error - try again.");
53192                                       
53193                         },
53194                         actioncomplete: function(re, act) {
53195                              
53196                             Roo.state.Manager.set(
53197                                 this.dialog.realm + '.username',  
53198                                     this.findField('username').getValue()
53199                             );
53200                             Roo.state.Manager.set(
53201                                 this.dialog.realm + '.lang',  
53202                                 this.findField('lang').getValue() 
53203                             );
53204                             
53205                             this.dialog.fillAuth(act.result.data);
53206                               
53207                             this.dialog.hide();
53208                             
53209                             if (Roo.get('loading-mask')) {
53210                                 Roo.get('loading-mask').show();
53211                             }
53212                             Roo.XComponent.build();
53213                             
53214                              
53215                             
53216                         }
53217                     },
53218                     items : [
53219                         {
53220                             xtype : 'TextField',
53221                             xns : Roo.form,
53222                             fieldLabel: "Email Address",
53223                             name: 'username',
53224                             width:200,
53225                             autoCreate : {tag: "input", type: "text", size: "20"}
53226                         },
53227                         {
53228                             xtype : 'TextField',
53229                             xns : Roo.form,
53230                             fieldLabel: "Password",
53231                             inputType: 'password',
53232                             name: 'password',
53233                             width:200,
53234                             autoCreate : {tag: "input", type: "text", size: "20"},
53235                             listeners : {
53236                                 specialkey : function(e,ev) {
53237                                     if (ev.keyCode == 13) {
53238                                         this.form.dialog.el.mask("Logging in");
53239                                         this.form.doAction('submit', {
53240                                             url: this.form.dialog.url,
53241                                             method: this.form.dialog.method
53242                                         });
53243                                     }
53244                                 }
53245                             }  
53246                         },
53247                         {
53248                             xtype : 'ComboBox',
53249                             xns : Roo.form,
53250                             fieldLabel: "Language",
53251                             name : 'langdisp',
53252                             store: {
53253                                 xtype : 'SimpleStore',
53254                                 fields: ['lang', 'ldisp'],
53255                                 data : [
53256                                     [ 'en', 'English' ],
53257                                     [ 'zh_HK' , '\u7E41\u4E2D' ],
53258                                     [ 'zh_CN', '\u7C21\u4E2D' ]
53259                                 ]
53260                             },
53261                             
53262                             valueField : 'lang',
53263                             hiddenName:  'lang',
53264                             width: 200,
53265                             displayField:'ldisp',
53266                             typeAhead: false,
53267                             editable: false,
53268                             mode: 'local',
53269                             triggerAction: 'all',
53270                             emptyText:'Select a Language...',
53271                             selectOnFocus:true,
53272                             listeners : {
53273                                 select :  function(cb, rec, ix) {
53274                                     this.form.switchLang(rec.data.lang);
53275                                 }
53276                             }
53277                         
53278                         }
53279                     ]
53280                 }
53281                   
53282                 
53283             ]
53284         }
53285     ],
53286     buttons : [
53287         {
53288             xtype : 'Button',
53289             xns : 'Roo',
53290             text : "Forgot Password",
53291             listeners : {
53292                 click : function() {
53293                     //console.log(this);
53294                     var n = this.form.findField('username').getValue();
53295                     if (!n.length) {
53296                         Roo.MessageBox.alert("Error", "Fill in your email address");
53297                         return;
53298                     }
53299                     Roo.Ajax.request({
53300                         url: this.dialog.url,
53301                         params: {
53302                             passwordRequest: n
53303                         },
53304                         method: this.dialog.method,
53305                         success:  function(response, opts)  {  // check successfull...
53306                         
53307                             var res = this.dialog.processResponse(response);
53308                             if (!res.success) { // error!
53309                                Roo.MessageBox.alert("Error" ,
53310                                     res.errorMsg ? res.errorMsg  : "Problem Requesting Password Reset");
53311                                return;
53312                             }
53313                             Roo.MessageBox.alert("Notice" ,
53314                                 "Please check you email for the Password Reset message");
53315                         },
53316                         failure : function() {
53317                             Roo.MessageBox.alert("Error" , "Problem Requesting Password Reset");
53318                         }
53319                         
53320                     });
53321                 }
53322             }
53323         },
53324         {
53325             xtype : 'Button',
53326             xns : 'Roo',
53327             text : "Login",
53328             listeners : {
53329                 
53330                 click : function () {
53331                         
53332                     this.dialog.el.mask("Logging in");
53333                     this.form.doAction('submit', {
53334                             url: this.dialog.url,
53335                             method: this.dialog.method
53336                     });
53337                 }
53338             }
53339         }
53340     ]
53341   
53342   
53343 })
53344  
53345
53346
53347